少年阿賓

          那些青春的歲月

            BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
            500 Posts :: 0 Stories :: 135 Comments :: 0 Trackbacks

          2016年5月13日 #

               摘要: 前陣子從支付寶轉(zhuǎn)賬1萬塊錢到余額寶,這是日常生活的一件普通小事,但作為互聯(lián)網(wǎng)研發(fā)人員的職業(yè)病,我就思考支付寶扣除1萬之后,如果系統(tǒng)掛掉怎么辦,這時余額寶賬戶并沒有增加1萬,數(shù)據(jù)就會出現(xiàn)不一致狀況了。上述場景在各個類型的系統(tǒng)中都能找到相似影子,比如在電商系統(tǒng)中,當(dāng)有用戶下單后,除了在訂單表插入一條記錄外,對應(yīng)商品表的這個商品數(shù)量必須減1吧,怎么保證?!在搜索廣告系統(tǒng)中,當(dāng)用戶點(diǎn)擊某廣告后,除了在點(diǎn)擊...  閱讀全文
          posted @ 2018-01-04 00:01 abin 閱讀(716) | 評論 (0)編輯 收藏

          微服務(wù)架構(gòu)采用Scale Cube方法設(shè)計(jì)應(yīng)用架構(gòu),將應(yīng)用服務(wù)按功能拆分成一組相互協(xié)作的服務(wù)。每個服務(wù)負(fù)責(zé)一組特定、相關(guān)的功能。每個服務(wù)可以有自己獨(dú)立的數(shù)據(jù)庫,從而保證與其他服務(wù)解耦。
          微服務(wù)優(yōu)點(diǎn)
          1、通過分解巨大單體式應(yīng)用為多個服務(wù)方法解決了復(fù)雜性問題,每個微服務(wù)相對較小
          2、每個單體應(yīng)用不局限于固定的技術(shù)棧,開發(fā)者可以自由選擇開發(fā)技術(shù),提供API服務(wù)。
          3、每個微服務(wù)獨(dú)立的開發(fā),部署
          4、單一職責(zé)功能,每個服務(wù)都很簡單,只關(guān)注于一個業(yè)務(wù)功能
          5、易于規(guī)?;_發(fā),多個開發(fā)團(tuán)隊(duì)可以并行開發(fā),每個團(tuán)隊(duì)負(fù)責(zé)一項(xiàng)服務(wù)
          6、改善故障隔離。一個服務(wù)宕機(jī)不會影響其他的服務(wù)
          微服務(wù)缺點(diǎn):
          1.開發(fā)者需要應(yīng)對創(chuàng)建分布式系統(tǒng)所產(chǎn)生的額外的復(fù)雜因素
          l  目前的IDE主要面對的是單體工程程序,無法顯示支持分布式應(yīng)用的開發(fā)
          l  測試工作更加困難
          l  需要采用服務(wù)間的通訊機(jī)制
          l  很難在不采用分布式事務(wù)的情況下跨服務(wù)實(shí)現(xiàn)功能
          l  跨服務(wù)實(shí)現(xiàn)要求功能要求團(tuán)隊(duì)之間的緊密協(xié)作
          2.部署復(fù)雜
          3.內(nèi)存占用量更高
          posted @ 2017-12-31 16:41 abin 閱讀(416) | 評論 (0)編輯 收藏

          JDK 的 HashMap 中使用了一個 hash 方法來做 bit shifting,在注釋中說明是為了防止一些實(shí)現(xiàn)比較差的hashCode() 方法,請問原理是什么?JDK 的源碼參見:GrepCode: java.util.HashMap (.java)
          /**
           * Applies a supplemental hash function to a given hashCode, which
           * defends against poor quality hash functions.  This is critical
           * because HashMap uses power-of-two length hash tables, that
           * otherwise encounter collisions for hashCodes that do not differ
           * in lower bits. Note: Null keys always map to hash 0, thus index 0.
           */
          static int hash(int h) {
              // This function ensures that hashCodes that differ only by
              // constant multiples at each bit position have a bounded
              // number of collisions (approximately 8 at default load factor).
              h ^= (h >>> 20) ^ (h >>> 12);
              return h ^ (h >>> 7) ^ (h >>> 4);
          }
          PS:網(wǎng)上看見有人說作者本人說原理需要參見圣經(jīng)《計(jì)算機(jī)程序設(shè)計(jì)藝術(shù)》的 Vol.3 里頭的介紹,不過木有看過神書,求達(dá)人介紹





          這段代碼叫“擾動函數(shù)”。
          題主貼的是Java 7的HashMap的源碼,Java 8中這步已經(jīng)簡化了,只做一次16位右位移異或混合,而不是四次,但原理是不變的。下面以Java 8的源碼為例解釋,

          //Java 8中的散列值優(yōu)化函數(shù)staticfinalinthash(Objectkey){inth;return(key==null)?0:(h=key.hashCode())^(h>>>16);//key.hashCode()為哈希算法,返回初始哈希值}
          大家都知道上面代碼里的key.hashCode()函數(shù)調(diào)用的是key鍵值類型自帶的哈希函數(shù),返回int型散列值。理論上散列值是一個int型,如果直接拿散列值作為下標(biāo)訪問HashMap主數(shù)組的話,考慮到2進(jìn)制32位帶符號的int表值范圍從-2147483648到2147483648。前后加起來大概40億的映射空間。只要哈希函數(shù)映射得比較均勻松散,一般應(yīng)用是很難出現(xiàn)碰撞的。但問題是一個40億長度的數(shù)組,內(nèi)存是放不下的。你想,HashMap擴(kuò)容之前的數(shù)組初始大小才16。所以這個散列值是不能直接拿來用的。用之前還要先做對數(shù)組的長度取模運(yùn)算,得到的余數(shù)才能用來訪問數(shù)組下標(biāo)。源碼中模運(yùn)算是在這個indexFor( )函數(shù)里完成的。

          bucketIndex = indexFor(hash, table.length);indexFor的代碼也很簡單,就是把散列值和數(shù)組長度做一個"與"操作,

          static int indexFor(int h, int length) {        return h & (length-1);}順便說一下,這也正好解釋了為什么HashMap的數(shù)組長度要取2的整次冪。因?yàn)檫@樣(數(shù)組長度-1)正好相當(dāng)于一個“低位掩碼”。“與”操作的結(jié)果就是散列值的高位全部歸零,只保留低位值,用來做數(shù)組下標(biāo)訪問。以初始長度16為例,16-1=15。2進(jìn)制表示是00000000 00000000 00001111。和某散列值做“與”操作如下,結(jié)果就是截取了最低的四位值。
          10100101 11000100 00100101& 00000000 00000000 00001111---------------------------------- 00000000 00000000 00000101    //高位全部歸零,只保留末四位
          但這時候問題就來了,這樣就算我的散列值分布再松散,要是只取最后幾位的話,碰撞也會很嚴(yán)重。更要命的是如果散列本身做得不好,分布上成等差數(shù)列的漏洞,恰好使最后幾個低位呈現(xiàn)規(guī)律性重復(fù),就無比蛋疼。這時候“擾動函數(shù)”的價值就體現(xiàn)出來了,說到這里大家應(yīng)該猜出來了。看下面這個圖,


          右位移16位,正好是32bit的一半,自己的高半?yún)^(qū)和低半?yún)^(qū)做異或,就是為了混合原始哈希碼的高位和低位,以此來加大低位的隨機(jī)性。而且混合后的低位摻雜了高位的部分特征,這樣高位的信息也被變相保留下來。最后我們來看一下PeterLawley的一篇專欄文章《An introduction to optimising a hashing strategy》里的的一個實(shí)驗(yàn):他隨機(jī)選取了352個字符串,在他們散列值完全沒有沖突的前提下,對它們做低位掩碼,取數(shù)組下標(biāo)。


          結(jié)果顯示,當(dāng)HashMap數(shù)組長度為512的時候,也就是用掩碼取低9位的時候,在沒有擾動函數(shù)的情況下,發(fā)生了103次碰撞,接近30%。而在使用了擾動函數(shù)之后只有92次碰撞。碰撞減少了將近10%??磥頂_動函數(shù)確實(shí)還是有功效的。但明顯Java 8覺得擾動做一次就夠了,做4次的話,多了可能邊際效用也不大,所謂為了效率考慮就改成一次了。
          ------------------------------------------------------








          https://www.zhihu.com/question/20733617



          posted @ 2017-12-24 22:38 abin 閱讀(444) | 評論 (0)編輯 收藏

          Go語言沒有沿襲傳統(tǒng)面向?qū)ο缶幊讨械闹T多概念,比如繼承、虛函數(shù)、構(gòu)造函數(shù)和析構(gòu)函數(shù)、隱藏的this指針等。

           

          方法

          Go 語言中同時有函數(shù)和方法。方法就是一個包含了接受者(receiver)的函數(shù),receiver可以是內(nèi)置類型或者結(jié)構(gòu)體類型的一個值或者是一個指針。所有給定類型的方法屬于該類型的方法集。

          如下面的這個例子,定義了一個新類型Integer,它和int一樣,只是為它內(nèi)置的int類型增加了個新方法Less()

          復(fù)制代碼
          type Integer int   func (a Integer) Less(b Integer) bool {     return a < b  }  func main() {     var a Integer = 1       if a.Less(2) {         fmt.Println("less then 2")     }    }
          復(fù)制代碼

          可以看出,Go語言在自定義類型的對象中沒有C++/Java那種隱藏的this指針,而是在定義成員方法時顯式聲明了其所屬的對象。

           

          method的語法如下:

          func (r ReceiverType) funcName(parameters) (results)

          當(dāng)調(diào)用method時,會將receiver作為函數(shù)的第一個參數(shù):

          funcName(r, parameters);

          所以,receiver是值類型還是指針類型要看method的作用。如果要修改對象的值,就需要傳遞對象的指針。

          指針作為Receiver會對實(shí)例對象的內(nèi)容發(fā)生操作,而普通類型作為Receiver僅僅是以副本作為操作對象,并不對原實(shí)例對象發(fā)生操作。

          復(fù)制代碼
          func (a *Ingeger) Add(b Integer) {     *a += b }  func main() {     var a Integer = 1      a.Add(3)     fmt.Println("a =", a)     //  a = 4 }
          復(fù)制代碼

          如果Add方法不使用指針,則a返回的結(jié)果不變,這是因?yàn)镚o語言函數(shù)的參數(shù)也是基于值傳遞。

          注意:當(dāng)方法的接受者是指針時,即使用值類型調(diào)用那么方法內(nèi)部也是對指針的操作。

           

          之前說過,Go語言沒有構(gòu)造函數(shù)的概念,通常使用一個全局函數(shù)來完成。例如:

          復(fù)制代碼
          func NewRect(x, y, width, height float64) *Rect {     return &Rect{x, y, width, height} }     func main() {     rect1 := NewRect(1,2,10,20)     fmt.Println(rect1.width) }
          復(fù)制代碼

           

           


          匿名組合

          Go語言提供了繼承,但是采用了組合的語法,我們將其稱為匿名組合,例如:

          復(fù)制代碼
          type Base struct {     name string }  func (base *Base) Set(myname string) {     base.name = myname }  func (base *Base) Get() string {     return base.name }  type Derived struct {     Base     age int  }  func (derived *Derived) Get() (nm string, ag int) {     return derived.name, derived.age }   func main() {     b := &Derived{}      b.Set("sina")     fmt.Println(b.Get()) }
          復(fù)制代碼

          例子中,在Base類型定義了get()和set()兩個方法,而Derived類型繼承了Base類,并改寫了Get()方法,在Derived對象調(diào)用Set()方法,會加載基類對應(yīng)的方法;而調(diào)用Get()方法時,加載派生類改寫的方法。

           

          組合的類型和被組合的類型包含同名成員時, 會不會有問題呢?可以參考下面的例子:

          復(fù)制代碼
          type Base struct {     name string     age int }  func (base *Base) Set(myname string, myage int) {     base.name = myname     base.age = myage }  type Derived struct {     Base     name string }  func main() {     b := &Derived{}      b.Set("sina", 30)     fmt.Println("b.name =",b.name, "\tb.Base.name =", b.Base.name)     fmt.Println("b.age =",b.age, "\tb.Base.age =", b.Base.age) }
          復(fù)制代碼

           

           

           


          值語義和引用語義

          值語義和引用語義的差別在于賦值,比如

          b = a b.Modify()

          如果b的修改不會影響a的值,那么此類型屬于值類型;如果會影響a的值,那么此類型是引用類型。

          Go語言中的大多數(shù)類型都基于值語義,包括:

          • 基本類型,如byte、int、bool、float32、string等;
          • 復(fù)合類型,如arry、struct、pointer等;

           

          C語言中的數(shù)組比較特別,通過函數(shù)傳遞一個數(shù)組的時候基于引用語義,但是在結(jié)構(gòu)體定義數(shù)組變量的時候基于值語義。而在Go語言中,數(shù)組和基本類型沒有區(qū)別,是很純粹的值類型,例如:

          var a = [3] int{1,2,3} var b = a b[1]++ fmt.Println(a, b)   // [1 2 3] [1 3 3]

          從結(jié)果看,b=a賦值語句是數(shù)組內(nèi)容的完整復(fù)制,要想表達(dá)引用,需要用指針:

          var a = [3] int{1,2,3} var b = &a    // 引用語義 b[1]++ fmt.Println(a, b)   // [1 3 3] [1 3 3]

           

           


          接口

          Interface 是一組抽象方法(未具體實(shí)現(xiàn)的方法/僅包含方法名參數(shù)返回值的方法)的集合,如果實(shí)現(xiàn)了 interface 中的所有方法,即該類/對象就實(shí)現(xiàn)了該接口。

          Interface 的聲明格式:

          type interfaceName interface {       //方法列表   }  

          Interface 可以被任意對象實(shí)現(xiàn),一個類型/對象也可以實(shí)現(xiàn)多個 interface;
          interface的變量可以持有任意實(shí)現(xiàn)該interface類型的對象。

           如下面的例子:

          復(fù)制代碼
          package main      import "fmt"      type Human struct {         name string         age int         phone string     }      type Student struct {         Human //匿名字段         school string         loan float32     }      type Employee struct {         Human //匿名字段         company string         money float32     }      //Human實(shí)現(xiàn)SayHi方法     func (h Human) SayHi() {         fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)     }      //Human實(shí)現(xiàn)Sing方法     func (h Human) Sing(lyrics string) {         fmt.Println("La la la la...", lyrics)     }      //Employee重載Human的SayHi方法     func (e Employee) SayHi() {         fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,             e.company, e.phone)         }      // Interface Men被Human,Student和Employee實(shí)現(xiàn)     // 因?yàn)檫@三個類型都實(shí)現(xiàn)了這兩個方法     type Men interface {         SayHi()         Sing(lyrics string)     }      func main() {         mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}         paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}         sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}         tom := Employee{Human{"Tom", 37, "222-444-XXX"}, "Things Ltd.", 5000}          //定義Men類型的變量i         var i Men          //i能存儲Student         i = mike             fmt.Println("This is Mike, a Student:")         i.SayHi()         i.Sing("November rain")          //i也能存儲Employee         i = tom         fmt.Println("This is tom, an Employee:")         i.SayHi()         i.Sing("Born to be wild")          //定義了slice Men         fmt.Println("Let's use a slice of Men and see what happens")         x := make([]Men, 3)         //這三個都是不同類型的元素,但是他們實(shí)現(xiàn)了interface同一個接口         x[0], x[1], x[2] = paul, sam, mike          for _, value := range x{             value.SayHi()         }     }
          復(fù)制代碼

           

          空接口

          空interface(interface{})不包含任何的method,正因?yàn)槿绱耍?span style="background-color: #ffff00;">所有的類型都實(shí)現(xiàn)了空interface??読nterface對于描述起不到任何的作用(因?yàn)樗话魏蔚膍ethod),但是空interface在我們需要存儲任意類型的數(shù)值的時候相當(dāng)有用,因?yàn)樗梢源鎯θ我忸愋偷臄?shù)值。它有點(diǎn)類似于C語言的void*類型。

          復(fù)制代碼
          // 定義a為空接口     var a interface{}     var i int = 5     s := "Hello world"     // a可以存儲任意類型的數(shù)值     a = i     a = s
          復(fù)制代碼

           

          interface的變量里面可以存儲任意類型的數(shù)值(該類型實(shí)現(xiàn)了interface),那么我們怎么反向知道這個interface變量里面實(shí)際保存了的是哪個類型的對象呢?目前常用的有兩種方法:switch測試、Comma-ok斷言。

           

          switch測試如下:

          復(fù)制代碼
          type Element interface{} type List [] Element  type Person struct {     name string     age int  }  //打印 func (p Person) String() string {     return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)" }  func main() {     list := make(List, 3)     list[0] = 1 //an int      list[1] = "Hello" //a string     list[2] = Person{"Dennis", 70}       for index, element := range list{         switch value := element.(type) {             case int:                 fmt.Printf("list[%d] is an int and its value is %d\n", index, value)             case string:                 fmt.Printf("list[%d] is a string and its value is %s\n", index, value)             case Person:                 fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)             default:                 fmt.Println("list[%d] is of a different type", index)         }        }    }
          復(fù)制代碼

           

          如果使用Comma-ok斷言的話:

          復(fù)制代碼
          func main() {     list := make(List, 3)     list[0] = 1 // an int     list[1] = "Hello" // a string     list[2] = Person{"Dennis", 70}      for index, element := range list {         if value, ok := element.(int); ok {             fmt.Printf("list[%d] is an int and its value is %d\n", index, value)         } else if value, ok := element.(string); ok {             fmt.Printf("list[%d] is a string and its value is %s\n", index, value)         } else if value, ok := element.(Person); ok {             fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)         } else {             fmt.Printf("list[%d] is of a different type\n", index)         }     } }
          復(fù)制代碼

           

           

          嵌入接口

          正如struct類型可以包含一個匿名字段,interface也可以嵌套另外一個接口。

          如果一個interface1作為interface2的一個嵌入字段,那么interface2隱式的包含了interface1里面的method。

           

           

          反射

          所謂反射(reflect)就是能檢查程序在運(yùn)行時的狀態(tài)。

          使用reflect一般分成三步,下面簡要的講解一下:要去反射是一個類型的值(這些值都實(shí)現(xiàn)了空interface),首先需要把它轉(zhuǎn)化成reflect對象(reflect.Type或者reflect.Value,根據(jù)不同的情況調(diào)用不同的函數(shù))。這兩種獲取方式如下:

           t := reflect.TypeOf(i)    //得到類型的元數(shù)據(jù),通過t我們能獲取類型定義里面的所有元素  v := reflect.ValueOf(i)   //得到實(shí)際的值,通過v我們獲取存儲在里面的值,還可以去改變值

           

          轉(zhuǎn)化為reflect對象之后我們就可以進(jìn)行一些操作了,也就是將reflect對象轉(zhuǎn)化成相應(yīng)的值,例如

          tag := t.Elem().Field(0).Tag  //獲取定義在struct里面的標(biāo)簽 name := v.Elem().Field(0).String()  //獲取存儲在第一個字段里面的值

           

          獲取反射值能返回相應(yīng)的類型和數(shù)值

          var x float64 = 3.4 v := reflect.ValueOf(x) fmt.Println("type:", v.Type()) fmt.Println("kind is float64:", v.Kind() == reflect.Float64) fmt.Println("value:", v.Float())

           

          最后,反射的話,那么反射的字段必須是可修改的,我們前面學(xué)習(xí)過傳值和傳引用,這個里面也是一樣的道理。反射的字段必須是可讀寫的意思是,如果下面這樣寫,那么會發(fā)生錯誤

          var x float64 = 3.4 v := reflect.ValueOf(x) v.SetFloat(7.1)

           

          如果要修改相應(yīng)的值,必須這樣寫

          var x float64 = 3.4 p := reflect.ValueOf(&x) v := p.Elem() v.SetFloat(7.1)

          上面只是對反射的簡單介紹,更深入的理解還需要自己在編程中不斷的實(shí)踐。

           

           

          參考文檔:

          http://se77en.cc/2014/05/05/methods-interfaces-and-embedded-types-in-golang/

          http://se77en.cc/2014/05/04/choose-whether-to-use-a-value-or-pointer-receiver-on-methods/

           http://www.cnblogs.com/chenny7/p/4497969.html





          posted @ 2017-08-03 11:34 abin 閱讀(442) | 評論 (0)編輯 收藏

          不可或缺的函數(shù),在Go中定義函數(shù)的方式如下:

          func (p myType ) funcName ( a, b int , c string ) ( r , s int ) {     return } 

          通過函數(shù)定義,我們可以看到Go中函數(shù)和其他語言中的共性和特性

          共性

          • 關(guān)鍵字——func
          • 方法名——funcName
          • 入?yún)?#8212;—— a,b int,b string
          • 返回值—— r,s int
          • 函數(shù)體—— {}

          特性

          Go中函數(shù)的特性是非??岬模o我們帶來不一樣的編程體驗(yàn)。

          為特定類型定義函數(shù),即為類型對象定義方法

          在Go中通過給函數(shù)標(biāo)明所屬類型,來給該類型定義方法,上面的 p myType 即表示給myType聲明了一個方法, p myType 不是必須的。如果沒有,則純粹是一個函數(shù),通過包名稱訪問。packageName.funcationName

          如:

          //定義新的類型double,主要目的是給float64類型擴(kuò)充方法 type double float64  //判斷a是否等于b func (a double) IsEqual(b double) bool {     var r = a - b     if r == 0.0 {         return true     } else if r < 0.0 {         return r > -0.0001     }     return r < 0.0001 }  //判斷a是否等于b func IsEqual(a, b float64) bool {     var r = a - b     if r == 0.0 {         return true     } else if r < 0.0 {         return r > -0.0001     }     return r < 0.0001 }  func main() {     var a double = 1.999999     var b double = 1.9999998     fmt.Println(a.IsEqual(b))     fmt.Println(a.IsEqual(3))     fmt.Println( IsEqual( (float64)(a), (float64)(b) ) )  } 

          上述示例為 float64 基本類型擴(kuò)充了方法IsEqual,該方法主要是解決精度問題。 其方法調(diào)用方式為: a.IsEqual(double) ,如果不擴(kuò)充方法,我們只能使用函數(shù)IsEqual(a, b float64)

          入?yún)⒅?,如果連續(xù)的參數(shù)類型一致,則可以省略連續(xù)多個參數(shù)的類型,只保留最后一個類型聲明。

          如 func IsEqual(a, b float64) bool 這個方法就只保留了一個類型聲明,此時入?yún)和b均是float64數(shù)據(jù)類型。 這樣也是可以的: func IsEqual(a, b float64, accuracy int) bool

          變參:入?yún)⒅С肿儏?即可接受不確定數(shù)量的同一類型的參數(shù)

          如 func Sum(args ...int) 參數(shù)args是的slice,其元素類型為int 。經(jīng)常使用的fmt.Printf就是一個接受任意個數(shù)參數(shù)的函數(shù) fmt.Printf(format string, args ...interface{})

          支持多返回值

          前面我們定義函數(shù)時返回值有兩個r,s 。這是非常有用的,我在寫C#代碼時,常常為了從已有函數(shù)中獲得更多的信息,需要修改函數(shù)簽名,使用out ,ref 等方式去獲得更多返回結(jié)果。而現(xiàn)在使用Go時則很簡單,直接在返回值后面添加返回參數(shù)即可。

          如,在C#中一個字符串轉(zhuǎn)換為int類型時邏輯代碼

          int v=0;  if ( int.TryPase("123456",out v) ) {     //code } 

          而在Go中,則可以這樣實(shí)現(xiàn),邏輯精簡而明確

          if v,isOk :=int.TryPase("123456") ; isOk {     //code } 

          同時在Go中很多函數(shù)充分利用了多返回值

          • func (file *File) Write(b []byte) (n int, err error)
          • func Sincos(x float64) (sin, cos float64)

          那么如果我只需要某一個返回值,而不關(guān)心其他返回值的話,我該如何辦呢? 這時可以簡單的使用符號下劃線”_“ 來忽略不關(guān)心的返回值。如:

          _, cos = math.Sincos(3.1415) //只需要cos計(jì)算的值 

          命名返回值

          前面我們說了函數(shù)可以有多個返回值,這里我還要說的是,在函數(shù)定義時可以給所有的返回值分別命名,這樣就能在函數(shù)中任意位置給不同返回值復(fù)制,而不需要在return語句中才指定返回值。同時也能增強(qiáng)可讀性,也提高godoc所生成文檔的可讀性

          如果不支持命名返回值,我可能會是這樣做的

          func ReadFull(r Reader, buf []byte) (int, error) {     var n int     var err error      for len(buf) > 0  {         var nr int         nr, err = r.Read(buf)          n += nr         if err !=nil {             return n,err         }         buf = buf[nr:]     }     return n,err } 

          但支持給返回值命名后,實(shí)際上就是省略了變量的聲明,return時無需寫成return n,err 而是將直接將值返回

          func ReadFull(r Reader, buf []byte) (n int, err error) {     for len(buf) > 0 && err == nil {         var nr int         nr, err = r.Read(buf)         n += nr         buf = buf[nr:]     }     return } 

          函數(shù)也是“值”

          和Go中其他東西一樣,函數(shù)也是值,這樣就可以聲明一個函數(shù)類型的變量,將函數(shù)作為參數(shù)傳遞。

          聲明函數(shù)為值的變量(匿名函數(shù):可賦值個變量,也可直接執(zhí)行)

          //賦值 fc := func(msg string) {     fmt.Println("you say :", msg) } fmt.Printf("%T \n", fc) fc("hello,my love") //直接執(zhí)行 func(msg string) {     fmt.Println("say :", msg) }("I love to code") 

          輸出結(jié)果如下,這里表明fc 的類型為:func(string)

          func(string)  you say : hello,my love say : I love to code 

          將函數(shù)作為入?yún)ⅲɑ卣{(diào)函數(shù)),能帶來便利。如日志處理,為了統(tǒng)一處理,將信息均通過指定函數(shù)去記錄日志,且是否記錄日志還有開關(guān)

          func Log(title string, getMsg func() string) {     //如果開啟日志記錄,則記錄日志     if true {         fmt.Println(title, ":", getMsg())     } } //---------調(diào)用-------------- count := 0 msg := func() string {     count++     return "您沒有即使提醒我,已觸犯法律" } Log("error", msg) Log("warring", msg) Log("info", msg) fmt.Println(count) 

          這里輸出結(jié)果如下,count 也發(fā)生了變化

          error : 您沒有即使提醒我,已觸犯法律 warring : 您沒有即使提醒我,已觸犯法律 info : 您沒有即使提醒我,已觸犯法律 3 

          函數(shù)也是“類型”

          你有沒有注意到上面示例中的 fc := func(msg string)... ,既然匿名函數(shù)可以賦值給一個變量,同時我們經(jīng)常這樣給int賦值 value := 2 ,是否我們可以聲明func(string) 類型 呢,當(dāng)然是可以的。

          //一個記錄日志的類型:func(string) type saveLog func(msg string)  //將字符串轉(zhuǎn)換為int64,如果轉(zhuǎn)換失敗調(diào)用saveLog func stringToInt(s string, log saveLog) int64 {      if value, err := strconv.ParseInt(s, 0, 0); err != nil {         log(err.Error())         return 0     } else {         return value     } }  //記錄日志消息的具體實(shí)現(xiàn) func myLog(msg string) {     fmt.Println("Find Error:", msg) }  func main() {     stringToInt("123", myLog) //轉(zhuǎn)換時將調(diào)用mylog記錄日志     stringToInt("s", myLog) } 

          這里我們定義了一個類型,專門用作記錄日志的標(biāo)準(zhǔn)接口。在stringToInt函數(shù)中如果轉(zhuǎn)換失敗則調(diào)用我自己定義的接口函數(shù)進(jìn)行日志處理,至于最終執(zhí)行的哪個函數(shù),則無需關(guān)心。

          defer 延遲函數(shù)

          defer 又是一個創(chuàng)新,它的作用是:延遲執(zhí)行,在聲明時不會立即執(zhí)行,而是在函數(shù)return后時按照后進(jìn)先出的原則依次執(zhí)行每一個defer。這樣帶來的好處是,能確保我們定義的函數(shù)能百分之百能夠被執(zhí)行到,這樣就能做很多我們想做的事,如釋放資源,清理數(shù)據(jù),記錄日志等

          這里我們重點(diǎn)來說明下defer的執(zhí)行順序

          func deferFunc() int {     index := 0      fc := func() {          fmt.Println(index, "匿名函數(shù)1")         index++          defer func() {             fmt.Println(index, "匿名函數(shù)1-1")             index++         }()     }      defer func() {         fmt.Println(index, "匿名函數(shù)2")         index++     }()      defer fc()      return func() int {         fmt.Println(index, "匿名函數(shù)3")         index++         return index     }() }  func main() {     deferFunc() } 

          這里輸出結(jié)果如下,

          0 匿名函數(shù)3 1 匿名函數(shù)1 2 匿名函數(shù)1-1 3 匿名函數(shù)2 

          有如下結(jié)論:

          • defer 是在執(zhí)行完return 后執(zhí)行
          • defer 后進(jìn)先執(zhí)行

          另外,我們常使用defer去關(guān)閉IO,在正常打開文件后,就立刻聲明一個defer,這樣就不會忘記關(guān)閉文件,也能保證在出現(xiàn)異常等不可預(yù)料的情況下也能關(guān)閉文件。而不像其他語言:try-catch 或者 using() 方式進(jìn)行處理。

          file , err :=os.Open(file) if err != nil {     return err } defer file.Close()  //dosomething with file 

          后續(xù),我將討論: 作用域、傳值和傳指針 以及 保留函數(shù)init(),main()

          本筆記中所寫代碼存儲位置:

          posted @ 2017-08-02 16:39 abin 閱讀(567) | 評論 (0)編輯 收藏

          MySQL觸發(fā)器Trigger實(shí)例篇
          發(fā)表于668 天前 ⁄ IT技術(shù) ⁄ 暫無評論

          以前關(guān)注的數(shù)據(jù)存儲過程不太懂其中奧妙,最近遇到跨數(shù)據(jù)庫,同時對多個表進(jìn)行CURD(Create增、Update改、Read讀、Delete刪),怎么才能讓繁瑣的數(shù)據(jù)CURD同步變得更容易呢?相信很多人會首先想到了MySQL存儲過程、觸發(fā)器,這種想法確實(shí)不錯。于是饒有興趣地親自寫了CUD(增、改、刪)觸發(fā)器的實(shí)例,用觸發(fā)器實(shí)現(xiàn)多表數(shù)據(jù)同步更新。

          MySQL觸發(fā)器Trigger實(shí)例篇

          定義: 何為MySQL觸發(fā)器?

          在MySQL Server里面也就是對某一個表的一定的操作,觸發(fā)某種條件(Insert,Update,Delete 等),從而自動執(zhí)行的一段程序。從這種意義上講觸發(fā)器是一個特殊的存儲過程。下面通過MySQL觸發(fā)器實(shí)例,來了解一下觸發(fā)器的工作過程吧!

          一、創(chuàng)建MySQL實(shí)例數(shù)據(jù)表:

          在mysql的默認(rèn)的測試test數(shù)據(jù)庫下,創(chuàng)建兩個表t_a與t_b:



              /*Table structure for table `t_a` */
              DROP TABLE IF EXISTS `t_a`;
              CREATE TABLE `t_a` (
                `id` smallint(1) unsigned NOT NULL AUTO_INCREMENT,
                `username` varchar(20) DEFAULT NULL,
                `groupid` mediumint(8) unsigned NOT NULL DEFAULT '0',
                PRIMARY KEY (`id`)
              ) ENGINE=MyISAM AUTO_INCREMENT=16 DEFAULT CHARSET=latin1;
               
              /*Data for the table `t_a` */
              LOCK TABLES `t_a` WRITE;
              UNLOCK TABLES;
               
              /*Table structure for table `t_b` */
              DROP TABLE IF EXISTS `t_b`;
              CREATE TABLE `t_b` (
                `id` smallint(1) unsigned NOT NULL AUTO_INCREMENT,
                `username` varchar(20) DEFAULT NULL,
                `groupid` mediumint(8) unsigned NOT NULL DEFAULT '0',
                PRIMARY KEY (`id`)
              ) ENGINE=MyISAM AUTO_INCREMENT=57 DEFAULT CHARSET=latin1;
               
              /*Data for the table `t_b` */
              LOCK TABLES `t_b` WRITE;
              UNLOCK TABLES;

          在t_a表上分創(chuàng)建一個CUD(增、改、刪)3個觸發(fā)器,將t_a的表數(shù)據(jù)與t_b同步實(shí)現(xiàn)CUD,注意創(chuàng)建觸發(fā)器每個表同類事件有且僅有一個對應(yīng)觸發(fā)器,為什么只能對一個觸發(fā)器,不解釋啦,看MYSQL的說明幫助文檔吧。

          二、創(chuàng)建MySQL實(shí)例觸發(fā)器:

          在實(shí)例數(shù)據(jù)表t_a上依次按照下面步驟創(chuàng)建tr_a_insert、tr_a_update、tr_a_delete三個觸發(fā)器

          1、創(chuàng)建INSERT觸發(fā)器trigger_a_insert:



              DELIMITER $$
               
              USE `test`$$
               
              --判斷數(shù)據(jù)庫中是否存在tr_a_insert觸發(fā)器
              DROP TRIGGER /*!50032 IF EXISTS */ `tr_a_insert`$$
              --不存在tr_a_insert觸發(fā)器,開始創(chuàng)建觸發(fā)器
              --Trigger觸發(fā)條件為insert成功后進(jìn)行觸發(fā)
              CREATE
                  /*!50017 DEFINER = 'root'@'localhost' */
                  TRIGGER `tr_a_insert` AFTER INSERT ON `t_a`
                  FOR EACH ROW BEGIN
                      --Trigger觸發(fā)后,同時對t_b新增同步一條數(shù)據(jù)
                      INSERT INTO `t_b` SET username = NEW.username, groupid=NEW.groupid;
                  END;
              $$
               
              DELIMITER;
          2、創(chuàng)建UPDATE觸發(fā)器trigger_a_update:


              DELIMITER $$
               
              USE `test`$$
              --判斷數(shù)據(jù)庫中是否存在tr_a_update觸發(fā)器
              DROP TRIGGER /*!50032 IF EXISTS */ `tr_a_update`$$
              --不存在tr_a_update觸發(fā)器,開始創(chuàng)建觸發(fā)器
              --Trigger觸發(fā)條件為update成功后進(jìn)行觸發(fā)
              CREATE
                  /*!50017 DEFINER = 'root'@'localhost' */
                  TRIGGER `tr_a_update` AFTER UPDATE ON `t_a`
                  FOR EACH ROW BEGIN
                  --Trigger觸發(fā)后,當(dāng)t_a表groupid,username數(shù)據(jù)有更改時,對t_b表同步一條更新后的數(shù)據(jù)
                    IF new.groupid != old.groupid OR old.username != new.username THEN
                      UPDATE `t_b` SET groupid=NEW.groupid,username=NEW.username WHEREusername=OLD.username AND groupid=OLD.groupid;
                    END IF;
                        
                  END;
              $$
               
              DELIMITER ;
          3、創(chuàng)建DELETE觸發(fā)器trigger_a_delete:


              DELIMITER $$
               
              USE `test`$$
              --判斷數(shù)據(jù)庫中是否存在tr_a_delete觸發(fā)器
              DROP TRIGGER /*!50032 IF EXISTS */ `tr_a_delete`$$
              --不存在tr_a_delete觸發(fā)器,開始創(chuàng)建觸發(fā)器
              --Trigger觸發(fā)條件為delete成功后進(jìn)行觸發(fā)
              CREATE
                  /*!50017 DEFINER = 'root'@'localhost' */
                  TRIGGER `tr_a_delete` AFTER DELETE ON `t_a`
                  FOR EACH ROW BEGIN
                      --t_a表數(shù)據(jù)刪除后,t_b表關(guān)聯(lián)條件相同的數(shù)據(jù)也同步刪除
                      DELETE FROM `t_b` WHERE username=Old.username AND groupid=OLD.groupid;
                  END;
              $$
               
              DELIMITER ;

          三、測試MySQL實(shí)例觸發(fā)器:

          分別測試實(shí)現(xiàn)t_a與t_b實(shí)現(xiàn)數(shù)據(jù)同步CUD(增、改、刪)3個Triggers

          1、測試MySQL的實(shí)例tr_a_insert觸發(fā)器:

          在t_a表中新增一條數(shù)據(jù),然后分別查詢t_a/t_b表的數(shù)據(jù)是否數(shù)據(jù)同步,測試觸發(fā)器成功標(biāo)志,t_a表無論在何種情況下,新增了一條或多條記錄集時,沒有t_b表做任何數(shù)據(jù)insert操作,它同時新增了一樣的多條記錄集。

          下面來進(jìn)行MySQL觸發(fā)器實(shí)例測試:



              --t_a表新增一條記錄集
                  INSERT INTO `t_a` (username,groupid) VALUES ('sky54.net',123)
                 
                  --查詢t_a表
                  SELECT id,username,groupid FROM `t_a`
                 
                  --查詢t_b表
                  SELECT id,username,groupid FROM `t_b`

          2、測試MySQL的實(shí)例tr_a_update、tr_a_delete觸發(fā)器:

          這兩個MySQL觸發(fā)器測試原理、步驟與tr_a_insert觸發(fā)器一樣的,先修改/刪除一條數(shù)據(jù),然后分別查看t_a、t_b表的數(shù)據(jù)變化情況,數(shù)據(jù)變化同步說明Trigger實(shí)例成功,否則需要逐步排查錯誤原因。

          世界上任何一種事物都其其優(yōu)點(diǎn)和缺點(diǎn),優(yōu)點(diǎn)與缺點(diǎn)是自身一個相對立的面。當(dāng)然這里不是強(qiáng)調(diào)“世界非黑即白”式的“二元論”,“存在即合理”嘛。當(dāng)然 MySQL觸發(fā)器的優(yōu)點(diǎn)不說了,說一下不足之處,MySQL Trigger沒有很好的調(diào)試、管理環(huán)境,難于在各種系統(tǒng)環(huán)境下測試,測試比MySQL存儲過程要難,所以建議在生成環(huán)境下,盡量用存儲過程來代替 MySQL觸發(fā)器。

          本篇結(jié)束前再強(qiáng)調(diào)一下,支持觸發(fā)器的MySQL版本需要5.0以上,5.0以前版本的MySQL升級到5.0以后版本方可使用觸發(fā)器哦!








          http://blog.csdn.net/hireboy/article/details/18079183



          posted @ 2016-08-18 17:25 abin 閱讀(1076) | 評論 (0)編輯 收藏

               摘要: 在開發(fā)高并發(fā)系統(tǒng)時有三把利器用來保護(hù)系統(tǒng):緩存、降級和限流。緩存的目的是提升系統(tǒng)訪問速度和增大系統(tǒng)能處理的容量,可謂是抗高并發(fā)流量的銀彈;而降級是當(dāng)服務(wù)出問題或者影響到核心流程的性能則需要暫時屏蔽掉,待高峰或者問題解決后再打開;而有些場景并不能用緩存和降級來解決,比如稀缺資源(秒殺、搶購)、寫服務(wù)(如評論、下單)、頻繁的復(fù)雜查詢(評論的最后幾頁),因此需有一種手段來限制這些場景的并發(fā)/請求量,即限...  閱讀全文
          posted @ 2016-06-14 13:38 abin 閱讀(2706) | 評論 (1)編輯 收藏

          Install the Command Line Client

          If you prefer command line client, then you can install it on your Linux with the following command.

          Debian

          sudo apt-get install python-pip sudo pip install shadowsocks

          Ubuntu

          Yes, you can use the above commands to install shadowsocks client on ubuntu. But it will install it under ~/.local/bin/ directory and it causes loads of trouble. So I suggest using su to become root first and then issue the following two commands.

          apt-get install python-pip pip install shadowsocks

          Fedora/Centos

          sudo yum install python-setuptools   or   sudo dnf install python-setuptools sudo easy_install pip sudo pip install shadowsocks

          OpenSUSE

          sudo zypper install python-pip sudo pip install shadowsocks

          Archlinux

          sudo pacman -S python-pip sudo pip install shadowsocks

          As you can see the command of installing shadowsocks client is the same to the command of installing shadowsocks server, because the above command will install both the client and the server. You can verify this by looking at the installation script output

          Downloading/unpacking shadowsocks Downloading shadowsocks-2.8.2.tar.gz Running setup.py (path:/tmp/pip-build-PQIgUg/shadowsocks/setup.py) egg_info for package shadowsocks  Installing collected packages: shadowsocks Running setup.py install for shadowsocks  Installing sslocal script to /usr/local/bin Installing ssserver script to /usr/local/bin Successfully installed shadowsocks Cleaning up...

          sslocal is the client software and ssserver is the server software. On some Linux distros such as ubuntu, the shadowsocks client sslocal is installed under /usr/local/bin. On Others such as Archsslocal is installed under /usr/bin/. Your can use whereis command to find the exact location.

          user@debian:~$ whereis sslocal sslocal: /usr/local/bin/sslocal

          Create a Configuration File

          we will create a configuration file under /etc/

          sudo vi /etc/shadowsocks.json

          Put the following text in the file. Replace server-ip with your actual IP and set a password.

          {
          "server":"server-ip",
          "server_port":8000,
          "local_address": "127.0.0.1",
          "local_port":1080,
          "password":"your-password",
          "timeout":600,
          "method":"aes-256-cfb"
          }

          Save and close the file. Next start the client using command line

          sslocal -c /etc/shadowsocks.json

          To run in the background

          sudo sslocal -c /etc/shadowsocks.json -d start

          Auto Start the Client on System Boot

          Edit /etc/rc.local file

          sudo vi /etc/rc.local

          Put the following line above the exit 0 line:

          sudo sslocal -c /etc/shadowsocks.json -d start

          Save and close the file. Next time you start your computer, shadowsocks client will automatically start and connect to your shadowsocks server.

          Check if It Works

          After you rebooted your computer, enter the following command in terminal:

          sudo systemctl status rc-local.service

          If your sslocal command works then you will get this ouput:


          ● rc-local.service - /etc/rc.local 

          Compatibility Loaded: loaded (/etc/systemd/system/rc-local.service; enabled; vendor preset: enabled)
          Active: active (running) since Fri 2015-11-27 03:19:25 CST; 2min 39s ago
          Process: 881 ExecStart=/etc/rc.local start (code=exited, status=0/SUCCESS)
          CGroup: /system.slice/rc-local.service
          ├─ 887 watch -n 60 su matrix -c ibam
          └─1112 /usr/bin/python /usr/local/bin/sslocal -c /etc/shadowsocks....

          As you can see from the last line, the sslocal command created a process whose pid is 1112 on my machine. It means shadowsocks client is running smoothly. And of course you can tell your browser to connect through your shadowsocks client to see if everything goes well.

          If for some reason your /etc/rc.local script won’t run, then check the following post to find the solution.

          How to enable /etc/rc.local with SystemdInstall the Command Line Client

          If you prefer command line client, then you can install it on your Linux with the following command.

          Debian

          sudo apt-get install python-pip
          sudo pip install shadowsocks

          Ubuntu

          Yes, you can use the above commands to install shadowsocks client on ubuntu. But it will install it under ~/.local/bin/ directory and it causes loads of trouble. So I suggest using su to become root first and then issue the following two commands.

          apt-get install python-pip
          pip install shadowsocks

          Fedora/Centos

          sudo yum install python-setuptools   or   sudo dnf install python-setuptools
          sudo easy_install pip
          sudo pip install shadowsocks

          OpenSUSE

          sudo zypper install python-pip
          sudo pip install shadowsocks

          Archlinux

          sudo pacman -S python-pip
          sudo pip install shadowsocks

          As you can see the command of installing shadowsocks client is the same to the command of installing shadowsocks server, because the above command will install both the client and the server. You can verify this by looking at the installation script output

          Downloading/unpacking shadowsocks
          Downloading shadowsocks-2.8.2.tar.gz
          Running setup.py (path:/tmp/pip-build-PQIgUg/shadowsocks/setup.py) egg_info for package shadowsocks
          
          Installing collected packages: shadowsocks
          Running setup.py install for shadowsocks
          
          Installing sslocal script to /usr/local/bin
          Installing ssserver script to /usr/local/bin
          Successfully installed shadowsocks
          Cleaning up...

          sslocal is the client software and ssserver is the server software. On some Linux distros such as ubuntu, the shadowsocks client sslocal is installed under /usr/local/bin. On Others such as Archsslocal is installed under /usr/bin/. Your can use whereis command to find the exact location.

          user@debian:~$ whereis sslocal
          sslocal: /usr/local/bin/sslocal

          Create a Configuration File

          we will create a configuration file under /etc/

          sudo vi /etc/shadowsocks.json

          Put the following text in the file. Replace server-ip with your actual IP and set a password.

          {
          "server":"server-ip",
          "server_port":8000,
          "local_address": "127.0.0.1",
          "local_port":1080,
          "password":"your-password",
          "timeout":600,
          "method":"aes-256-cfb"
          }

          Save and close the file. Next start the client using command line

          sslocal -c /etc/shadowsocks.json

          To run in the background

          sudo sslocal -c /etc/shadowsocks.json -d start

          Auto Start the Client on System Boot

          Edit /etc/rc.local file

          sudo vi /etc/rc.local

          Put the following line above the exit 0 line:

          sudo sslocal -c /etc/shadowsocks.json -d start

          Save and close the file. Next time you start your computer, shadowsocks client will automatically start and connect to your shadowsocks server.

          Check if It Works

          After you rebooted your computer, enter the following command in terminal:

          sudo systemctl status rc-local.service

          If your sslocal command works then you will get this ouput:

          ● rc-local.service - /etc/rc.local Compatibility
          Loaded: loaded (/etc/systemd/system/rc-local.service; enabled; vendor preset: enabled)
          Active: active (running) since Fri 2015-11-27 03:19:25 CST; 2min 39s ago
          Process: 881 ExecStart=/etc/rc.local start (code=exited, status=0/SUCCESS)
          CGroup: /system.slice/rc-local.service
          ├─ 887 watch -n 60 su matrix -c ibam
          └─1112 /usr/bin/python /usr/local/bin/sslocal -c /etc/shadowsocks....

          As you can see from the last line, the sslocal command created a process whose pid is 1112 on my machine. It means shadowsocks client is running smoothly. And of course you can tell your browser to connect through your shadowsocks client to see if everything goes well.

          If for some reason your /etc/rc.local script won’t run, then check the following post to find the solution.

          How to enable /etc/rc.local with Systemd




          posted @ 2016-05-13 22:56 abin 閱讀(801) | 評論 (0)編輯 收藏

          主站蜘蛛池模板: 亚东县| 达孜县| 竹山县| 永宁县| 思南县| 米泉市| 玉溪市| 泰来县| 江安县| 莒南县| 宜宾县| 湾仔区| 刚察县| 玛沁县| 舟曲县| 沽源县| 沧源| 柳河县| 湘潭县| 肥西县| 松原市| 玉屏| 玉树县| 六盘水市| 南丹县| 龙泉市| 务川| 宜章县| 巍山| 金寨县| 英超| 芜湖市| 保康县| 喀什市| 清流县| 博白县| 来宾市| 达孜县| 澄城县| 务川| 沙湾县|