2018年1月4日
#
摘要: 前陣子從支付寶轉(zhuǎn)賬1萬塊錢到余額寶,這是日常生活的一件普通小事,但作為互聯(lián)網(wǎng)研發(fā)人員的職業(yè)病,我就思考支付寶扣除1萬之后,如果系統(tǒng)掛掉怎么辦,這時余額寶賬戶并沒有增加1萬,數(shù)據(jù)就會出現(xiàn)不一致狀況了。上述場景在各個類型的系統(tǒng)中都能找到相似影子,比如在電商系統(tǒng)中,當有用戶下單后,除了在訂單表插入一條記錄外,對應(yīng)商品表的這個商品數(shù)量必須減1吧,怎么保證?!在搜索廣告系統(tǒng)中,當用戶點擊某廣告后,除了在點擊... 閱讀全文
2017年12月31日
#
微服務(wù)架構(gòu)采用Scale Cube方法設(shè)計應(yīng)用架構(gòu),將應(yīng)用服務(wù)按功能拆分成一組相互協(xié)作的服務(wù)。每個服務(wù)負責(zé)一組特定、相關(guān)的功能。每個服務(wù)可以有自己獨立的數(shù)據(jù)庫,從而保證與其他服務(wù)解耦。 微服務(wù)優(yōu)點 1、通過分解巨大單體式應(yīng)用為多個服務(wù)方法解決了復(fù)雜性問題,每個微服務(wù)相對較小 2、每個單體應(yīng)用不局限于固定的技術(shù)棧,開發(fā)者可以自由選擇開發(fā)技術(shù),提供API服務(wù)。 3、每個微服務(wù)獨立的開發(fā),部署 4、單一職責(zé)功能,每個服務(wù)都很簡單,只關(guān)注于一個業(yè)務(wù)功能 5、易于規(guī)模化開發(fā),多個開發(fā)團隊可以并行開發(fā),每個團隊負責(zé)一項服務(wù) 6、改善故障隔離。一個服務(wù)宕機不會影響其他的服務(wù) 微服務(wù)缺點: 1.開發(fā)者需要應(yīng)對創(chuàng)建分布式系統(tǒng)所產(chǎn)生的額外的復(fù)雜因素 l 目前的IDE主要面對的是單體工程程序,無法顯示支持分布式應(yīng)用的開發(fā) l 測試工作更加困難 l 需要采用服務(wù)間的通訊機制 l 很難在不采用分布式事務(wù)的情況下跨服務(wù)實現(xiàn)功能 l 跨服務(wù)實現(xiàn)要求功能要求團隊之間的緊密協(xié)作 2.部署復(fù)雜 3.內(nèi)存占用量更高
2017年12月24日
#
JDK 的 HashMap 中使用了一個 hash 方法來做 bit shifting,在注釋中說明是為了防止一些實現(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)《計算機程序設(shè)計藝術(shù)》的 Vol.3 里頭的介紹,不過木有看過神書,求達人介紹 這段代碼叫“擾動函數(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型,如果直接拿散列值作為下標訪問HashMap主數(shù)組的話,考慮到2進制32位帶符號的int表值范圍從-2147483648到2147483648。前后加起來大概40億的映射空間。只要哈希函數(shù)映射得比較均勻松散,一般應(yīng)用是很難出現(xiàn)碰撞的。但問題是一個40億長度的數(shù)組,內(nèi)存是放不下的。你想,HashMap擴容之前的數(shù)組初始大小才16。所以這個散列值是不能直接拿來用的。用之前還要先做對數(shù)組的長度取模運算,得到的余數(shù)才能用來訪問數(shù)組下標。源碼中模運算是在這個indexFor( )函數(shù)里完成的。 bucketIndex = indexFor(hash, table.length);indexFor的代碼也很簡單,就是把散列值和數(shù)組長度做一個"與"操作, static int indexFor(int h, int length) { return h & (length-1);}順便說一下,這也正好解釋了為什么HashMap的數(shù)組長度要取2的整次冪。因為這樣(數(shù)組長度-1)正好相當于一個“低位掩碼”。“與”操作的結(jié)果就是散列值的高位全部歸零,只保留低位值,用來做數(shù)組下標訪問。以初始長度16為例,16-1=15。2進制表示是00000000 00000000 00001111。和某散列值做“與”操作如下,結(jié)果就是截取了最低的四位值。 10100101 11000100 00100101& 00000000 00000000 00001111---------------------------------- 00000000 00000000 00000101 //高位全部歸零,只保留末四位 但這時候問題就來了,這樣就算我的散列值分布再松散,要是只取最后幾位的話,碰撞也會很嚴重。更要命的是如果散列本身做得不好,分布上成等差數(shù)列的漏洞,恰好使最后幾個低位呈現(xiàn)規(guī)律性重復(fù),就無比蛋疼。這時候“擾動函數(shù)”的價值就體現(xiàn)出來了,說到這里大家應(yīng)該猜出來了。看下面這個圖,  右位移16位,正好是32bit的一半,自己的高半?yún)^(qū)和低半?yún)^(qū)做異或,就是為了混合原始哈希碼的高位和低位,以此來加大低位的隨機性。而且混合后的低位摻雜了高位的部分特征,這樣高位的信息也被變相保留下來。最后我們來看一下PeterLawley的一篇專欄文章《An introduction to optimising a hashing strategy》里的的一個實驗:他隨機選取了352個字符串,在他們散列值完全沒有沖突的前提下,對它們做低位掩碼,取數(shù)組下標。  結(jié)果顯示,當HashMap數(shù)組長度為512的時候,也就是用掩碼取低9位的時候,在沒有擾動函數(shù)的情況下,發(fā)生了103次碰撞,接近30%。而在使用了擾動函數(shù)之后只有92次碰撞。碰撞減少了將近10%。看來擾動函數(shù)確實還是有功效的。但明顯Java 8覺得擾動做一次就夠了,做4次的話,多了可能邊際效用也不大,所謂為了效率考慮就改成一次了。 ------------------------------------------------------ https://www.zhihu.com/question/20733617
2017年8月3日
#
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() 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") } } 可以看出,Go語言在自定義類型的對象中沒有C++/Java那種隱藏的this指針,而是在定義成員方法時顯式聲明了其所屬的對象。 method的語法如下: func (r ReceiverType) funcName(parameters) (results) 當調(diào)用method時,會將receiver作為函數(shù)的第一個參數(shù): 所以,receiver是值類型還是指針類型要看method的作用。如果要修改對象的值,就需要傳遞對象的指針。 指針作為Receiver會對實例對象的內(nèi)容發(fā)生操作,而普通類型作為Receiver僅僅是以副本作為操作對象,并不對原實例對象發(fā)生操作。 func (a *Ingeger) Add(b Integer) { *a += b } func main() { var a Integer = 1 a.Add(3) fmt.Println("a =", a) // a = 4 } 如果Add方法不使用指針,則a返回的結(jié)果不變,這是因為Go語言函數(shù)的參數(shù)也是基于值傳遞。 注意:當方法的接受者是指針時,即使用值類型調(diào)用那么方法內(nèi)部也是對指針的操作。 之前說過,Go語言沒有構(gòu)造函數(shù)的概念,通常使用一個全局函數(shù)來完成。例如: 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) } 匿名組合 Go語言提供了繼承,但是采用了組合的語法,我們將其稱為匿名組合,例如: 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()) } 例子中,在Base類型定義了get()和set()兩個方法,而Derived類型繼承了Base類,并改寫了Get()方法,在Derived對象調(diào)用Set()方法,會加載基類對應(yīng)的方法;而調(diào)用Get()方法時,加載派生類改寫的方法。 組合的類型和被組合的類型包含同名成員時, 會不會有問題呢?可以參考下面的例子: 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) } 值語義和引用語義 值語義和引用語義的差別在于賦值,比如 如果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ù)制,要想表達引用,需要用指針: var a = [3] int{1,2,3} var b = &a // 引用語義 b[1]++ fmt.Println(a, b) // [1 3 3] [1 3 3] 接口 Interface 是一組抽象方法(未具體實現(xiàn)的方法/僅包含方法名參數(shù)返回值的方法)的集合,如果實現(xiàn)了 interface 中的所有方法,即該類/對象就實現(xiàn)了該接口。 Interface 的聲明格式: type interfaceName interface { //方法列表 } Interface 可以被任意對象實現(xiàn),一個類型/對象也可以實現(xiàn)多個 interface; interface的變量可以持有任意實現(xiàn)該interface類型的對象。 如下面的例子:  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實現(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實現(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實現(xiàn) // 因為這三個類型都實現(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) //這三個都是不同類型的元素,但是他們實現(xiàn)了interface同一個接口 x[0], x[1], x[2] = paul, sam, mike for _, value := range x{ value.SayHi() } }  空接口 空interface(interface{})不包含任何的method,正因為如此,所有的類型都實現(xiàn)了空interface。空interface對于描述起不到任何的作用(因為它不包含任何的method),但是空interface在我們需要存儲任意類型的數(shù)值的時候相當有用,因為它可以存儲任意類型的數(shù)值。它有點類似于C語言的void*類型。 // 定義a為空接口 var a interface{} var i int = 5 s := "Hello world" // a可以存儲任意類型的數(shù)值 a = i a = s interface的變量里面可以存儲任意類型的數(shù)值(該類型實現(xiàn)了interface),那么我們怎么反向知道這個interface變量里面實際保存了的是哪個類型的對象呢?目前常用的有兩種方法:switch測試、Comma-ok斷言。 switch測試如下: 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) } } } 如果使用Comma-ok斷言的話: 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) } } } 嵌入接口 正如struct類型可以包含一個匿名字段,interface也可以嵌套另外一個接口。 如果一個interface1作為interface2的一個嵌入字段,那么interface2隱式的包含了interface1里面的method。 反射 所謂反射(reflect)就是能檢查程序在運行時的狀態(tài)。 使用reflect一般分成三步,下面簡要的講解一下:要去反射是一個類型的值(這些值都實現(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) //得到實際的值,通過v我們獲取存儲在里面的值,還可以去改變值 轉(zhuǎn)化為reflect對象之后我們就可以進行一些操作了,也就是將reflect對象轉(zhuǎn)化成相應(yīng)的值,例如 tag := t.Elem().Field(0).Tag //獲取定義在struct里面的標簽 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) 上面只是對反射的簡單介紹,更深入的理解還需要自己在編程中不斷的實踐。 參考文檔: 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
2017年8月2日
#
不可或缺的函數(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ù)的特性是非常酷的,給我們帶來不一樣的編程體驗。 為特定類型定義函數(shù),即為類型對象定義方法在Go中通過給函數(shù)標明所屬類型,來給該類型定義方法,上面的 p myType 即表示給myType聲明了一個方法, p myType 不是必須的。如果沒有,則純粹是一個函數(shù),通過包名稱訪問。packageName.funcationName 如: //定義新的類型double,主要目的是給float64類型擴充方法 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 基本類型擴充了方法IsEqual,該方法主要是解決精度問題。 其方法調(diào)用方式為: a.IsEqual(double) ,如果不擴充方法,我們只能使用函數(shù)IsEqual(a, b float64) 入?yún)⒅校绻B續(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中,則可以這樣實現(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計算的值
命名返回值前面我們說了函數(shù)可以有多個返回值,這里我還要說的是,在函數(shù)定義時可以給所有的返回值分別命名,這樣就能在函數(shù)中任意位置給不同返回值復(fù)制,而不需要在return語句中才指定返回值。同時也能增強可讀性,也提高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 }
但支持給返回值命名后,實際上就是省略了變量的聲明,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) 類型 呢,當然是可以的。 //一個記錄日志的類型: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 } } //記錄日志消息的具體實現(xiàn) func myLog(msg string) { fmt.Println("Find Error:", msg) } func main() { stringToInt("123", myLog) //轉(zhuǎn)換時將調(diào)用mylog記錄日志 stringToInt("s", myLog) }
這里我們定義了一個類型,專門用作記錄日志的標準接口。在stringToInt函數(shù)中如果轉(zhuǎn)換失敗則調(diào)用我自己定義的接口函數(shù)進行日志處理,至于最終執(zhí)行的哪個函數(shù),則無需關(guān)心。 defer 延遲函數(shù)defer 又是一個創(chuàng)新,它的作用是:延遲執(zhí)行,在聲明時不會立即執(zhí)行,而是在函數(shù)return后時按照后進先出的原則依次執(zhí)行每一個defer。這樣帶來的好處是,能確保我們定義的函數(shù)能百分之百能夠被執(zhí)行到,這樣就能做很多我們想做的事,如釋放資源,清理數(shù)據(jù),記錄日志等 這里我們重點來說明下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 后進先執(zhí)行
另外,我們常使用defer去關(guān)閉IO,在正常打開文件后,就立刻聲明一個defer,這樣就不會忘記關(guān)閉文件,也能保證在出現(xiàn)異常等不可預(yù)料的情況下也能關(guān)閉文件。而不像其他語言:try-catch 或者 using() 方式進行處理。 file , err :=os.Open(file) if err != nil { return err } defer file.Close() //dosomething with file
后續(xù),我將討論: 作用域、傳值和傳指針 以及 保留函數(shù)init(),main() 本筆記中所寫代碼存儲位置:
2016年8月18日
#
以前關(guān)注的數(shù)據(jù)存儲過程不太懂其中奧妙,最近遇到跨數(shù)據(jù)庫,同時對多個表進行CURD(Create增、Update改、Read讀、Delete刪),怎么才能讓繁瑣的數(shù)據(jù)CURD同步變得更容易呢?相信很多人會首先想到了MySQL存儲過程、觸發(fā)器,這種想法確實不錯。于是饒有興趣地親自寫了CUD(增、改、刪)觸發(fā)器的實例,用觸發(fā)器實現(xiàn)多表數(shù)據(jù)同步更新。  定義: 何為MySQL觸發(fā)器? 在MySQL Server里面也就是對某一個表的一定的操作,觸發(fā)某種條件(Insert,Update,Delete 等),從而自動執(zhí)行的一段程序。從這種意義上講觸發(fā)器是一個特殊的存儲過程。下面通過MySQL觸發(fā)器實例,來了解一下觸發(fā)器的工作過程吧! 一、創(chuàng)建MySQL實例數(shù)據(jù)表: 在mysql的默認的測試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同步實現(xiàn)CUD,注意創(chuàng)建觸發(fā)器每個表同類事件有且僅有一個對應(yīng)觸發(fā)器,為什么只能對一個觸發(fā)器,不解釋啦,看MYSQL的說明幫助文檔吧。 二、創(chuàng)建MySQL實例觸發(fā)器: 在實例數(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成功后進行觸發(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成功后進行觸發(fā) CREATE /*!50017 DEFINER = 'root'@'localhost' */ TRIGGER `tr_a_update` AFTER UPDATE ON `t_a` FOR EACH ROW BEGIN --Trigger觸發(fā)后,當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成功后進行觸發(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實例觸發(fā)器: 分別測試實現(xiàn)t_a與t_b實現(xiàn)數(shù)據(jù)同步CUD(增、改、刪)3個Triggers 1、測試MySQL的實例tr_a_insert觸發(fā)器: 在t_a表中新增一條數(shù)據(jù),然后分別查詢t_a/t_b表的數(shù)據(jù)是否數(shù)據(jù)同步,測試觸發(fā)器成功標志,t_a表無論在何種情況下,新增了一條或多條記錄集時,沒有t_b表做任何數(shù)據(jù)insert操作,它同時新增了一樣的多條記錄集。 下面來進行MySQL觸發(fā)器實例測試:
--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的實例tr_a_update、tr_a_delete觸發(fā)器: 這兩個MySQL觸發(fā)器測試原理、步驟與tr_a_insert觸發(fā)器一樣的,先修改/刪除一條數(shù)據(jù),然后分別查看t_a、t_b表的數(shù)據(jù)變化情況,數(shù)據(jù)變化同步說明Trigger實例成功,否則需要逐步排查錯誤原因。 世界上任何一種事物都其其優(yōu)點和缺點,優(yōu)點與缺點是自身一個相對立的面。當然這里不是強調(diào)“世界非黑即白”式的“二元論”,“存在即合理”嘛。當然 MySQL觸發(fā)器的優(yōu)點不說了,說一下不足之處,MySQL Trigger沒有很好的調(diào)試、管理環(huán)境,難于在各種系統(tǒng)環(huán)境下測試,測試比MySQL存儲過程要難,所以建議在生成環(huán)境下,盡量用存儲過程來代替 MySQL觸發(fā)器。 本篇結(jié)束前再強調(diào)一下,支持觸發(fā)器的MySQL版本需要5.0以上,5.0以前版本的MySQL升級到5.0以后版本方可使用觸發(fā)器哦! http://blog.csdn.net/hireboy/article/details/18079183
2016年6月14日
#
摘要: 在開發(fā)高并發(fā)系統(tǒng)時有三把利器用來保護系統(tǒng):緩存、降級和限流。緩存的目的是提升系統(tǒng)訪問速度和增大系統(tǒng)能處理的容量,可謂是抗高并發(fā)流量的銀彈;而降級是當服務(wù)出問題或者影響到核心流程的性能則需要暫時屏蔽掉,待高峰或者問題解決后再打開;而有些場景并不能用緩存和降級來解決,比如稀缺資源(秒殺、搶購)、寫服務(wù)(如評論、下單)、頻繁的復(fù)雜查詢(評論的最后幾頁),因此需有一種手段來限制這些場景的并發(fā)/請求量,即限... 閱讀全文
2016年5月13日
#
Install the Command Line ClientIf you prefer command line client, then you can install it on your Linux with the following command. Debiansudo apt-get install python-pip sudo pip install shadowsocks UbuntuYes, 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/Centossudo yum install python-setuptools or sudo dnf install python-setuptools sudo easy_install pip sudo pip install shadowsocks OpenSUSEsudo zypper install python-pip sudo pip install shadowsocks Archlinuxsudo 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 Filewe 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 backgroundsudo sslocal -c /etc/shadowsocks.json -d start Auto Start the Client on System BootEdit /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 WorksAfter 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. If you prefer command line client, then you can install it on your Linux with the following command. Debiansudo apt-get install python-pip
sudo pip install shadowsocks
UbuntuYes, 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/Centossudo yum install python-setuptools or sudo dnf install python-setuptools
sudo easy_install pip
sudo pip install shadowsocks
OpenSUSEsudo zypper install python-pip
sudo pip install shadowsocks
Archlinuxsudo 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 Filewe 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 backgroundsudo sslocal -c /etc/shadowsocks.json -d start
Auto Start the Client on System BootEdit /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 WorksAfter 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
2016年4月27日
#
廢話少說,直接上代碼,以前都是調(diào)用別人寫好的,現(xiàn)在有時間自己弄下,具體功能如下: 1、httpClient+http+線程池: 2、httpClient+https(單向不驗證證書)+線程池: https在%TOMCAT_HOME%/conf/server.xml里面的配置文件 <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" clientAuth="false" keystoreFile="D:/tomcat.keystore" keystorePass="heikaim" sslProtocol="TLS" executor="tomcatThreadPool"/> 其中 clientAuth="false"表示不開啟證書驗證,只是單存的走https package com.abin.lee.util;
import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.*; import org.apache.http.client.HttpRequestRetryHandler; import org.apache.http.client.config.CookieSpecs; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils;
import javax.net.ssl.*; import java.io.IOException; import java.io.InterruptedIOException; import java.net.UnknownHostException; import java.nio.charset.Charset; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.*;
/** * Created with IntelliJ IDEA. * User: abin * Date: 16-4-18 * Time: 上午10:24 * To change this template use File | Settings | File Templates. */ public class HttpClientUtil { private static CloseableHttpClient httpsClient = null; private static CloseableHttpClient httpClient = null;
static { httpClient = getHttpClient(); httpsClient = getHttpsClient(); }
public static CloseableHttpClient getHttpClient() { try { httpClient = HttpClients.custom() .setConnectionManager(PoolManager.getHttpPoolInstance()) .setConnectionManagerShared(true) .setDefaultRequestConfig(requestConfig()) .setRetryHandler(retryHandler()) .build(); } catch (Exception e) { e.printStackTrace(); } return httpClient; }
public static CloseableHttpClient getHttpsClient() { try { //Secure Protocol implementation. SSLContext ctx = SSLContext.getInstance("SSL"); //Implementation of a trust manager for X509 certificates TrustManager x509TrustManager = new X509TrustManager() { public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } }; ctx.init(null, new TrustManager[]{x509TrustManager}, null); //首先設(shè)置全局的標準cookie策略 // RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT).build(); ConnectionSocketFactory connectionSocketFactory = new SSLConnectionSocketFactory(ctx, hostnameVerifier); Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", connectionSocketFactory).build(); // 設(shè)置連接池 httpsClient = HttpClients.custom() .setConnectionManager(PoolsManager.getHttpsPoolInstance(socketFactoryRegistry)) .setConnectionManagerShared(true) .setDefaultRequestConfig(requestConfig()) .setRetryHandler(retryHandler()) .build(); } catch (Exception e) { e.printStackTrace(); } return httpsClient; }
// 配置請求的超時設(shè)置 //首先設(shè)置全局的標準cookie策略 public static RequestConfig requestConfig(){ RequestConfig requestConfig = RequestConfig.custom() .setCookieSpec(CookieSpecs.STANDARD_STRICT) .setConnectionRequestTimeout(20000) .setConnectTimeout(20000) .setSocketTimeout(20000) .build(); return requestConfig; }
public static HttpRequestRetryHandler retryHandler(){ //請求重試處理 HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() { public boolean retryRequest(IOException exception,int executionCount, HttpContext context) { if (executionCount >= 5) {// 如果已經(jīng)重試了5次,就放棄 return false; } if (exception instanceof NoHttpResponseException) {// 如果服務(wù)器丟掉了連接,那么就重試 return true; } if (exception instanceof SSLHandshakeException) {// 不要重試SSL握手異常 return false; } if (exception instanceof InterruptedIOException) {// 超時 return false; } if (exception instanceof UnknownHostException) {// 目標服務(wù)器不可達 return false; } if (exception instanceof ConnectTimeoutException) {// 連接被拒絕 return false; } if (exception instanceof SSLException) {// ssl握手異常 return false; }
HttpClientContext clientContext = HttpClientContext.adapt(context); HttpRequest request = clientContext.getRequest(); // 如果請求是冪等的,就再次嘗試 if (!(request instanceof HttpEntityEnclosingRequest)) { return true; } return false; } }; return httpRequestRetryHandler; }
//創(chuàng)建HostnameVerifier //用于解決javax.net.ssl.SSLException: hostname in certificate didn't match: <123.125.97.66> != <123.125.97.241> static HostnameVerifier hostnameVerifier = new NoopHostnameVerifier(){ @Override public boolean verify(String s, SSLSession sslSession) { return super.verify(s, sslSession); } };
public static class PoolManager { public static PoolingHttpClientConnectionManager clientConnectionManager = null; private static int maxTotal = 200; private static int defaultMaxPerRoute = 100;
private PoolManager(){ clientConnectionManager.setMaxTotal(maxTotal); clientConnectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute); }
private static class PoolManagerHolder{ public static PoolManager instance = new PoolManager(); }
public static PoolManager getInstance() { if(null == clientConnectionManager) clientConnectionManager = new PoolingHttpClientConnectionManager(); return PoolManagerHolder.instance; }
public static PoolingHttpClientConnectionManager getHttpPoolInstance() { PoolManager.getInstance(); // System.out.println("getAvailable=" + clientConnectionManager.getTotalStats().getAvailable()); // System.out.println("getLeased=" + clientConnectionManager.getTotalStats().getLeased()); // System.out.println("getMax=" + clientConnectionManager.getTotalStats().getMax()); // System.out.println("getPending="+clientConnectionManager.getTotalStats().getPending()); return PoolManager.clientConnectionManager; }
}
public static class PoolsManager { public static PoolingHttpClientConnectionManager clientConnectionManager = null; private static int maxTotal = 200; private static int defaultMaxPerRoute = 100;
private PoolsManager(){ clientConnectionManager.setMaxTotal(maxTotal); clientConnectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute); }
private static class PoolsManagerHolder{ public static PoolsManager instance = new PoolsManager(); }
public static PoolsManager getInstance(Registry<ConnectionSocketFactory> socketFactoryRegistry) { if(null == clientConnectionManager) clientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); return PoolsManagerHolder.instance; }
public static PoolingHttpClientConnectionManager getHttpsPoolInstance(Registry<ConnectionSocketFactory> socketFactoryRegistry) { PoolsManager.getInstance(socketFactoryRegistry); // System.out.println("getAvailable=" + clientConnectionManager.getTotalStats().getAvailable()); // System.out.println("getLeased=" + clientConnectionManager.getTotalStats().getLeased()); // System.out.println("getMax=" + clientConnectionManager.getTotalStats().getMax()); // System.out.println("getPending="+clientConnectionManager.getTotalStats().getPending()); return PoolsManager.clientConnectionManager; }
}
public static String httpPost(Map<String, String> request, String httpUrl){ String result = ""; CloseableHttpClient httpClient = getHttpClient(); try { if(MapUtils.isEmpty(request)) throw new Exception("請求參數(shù)不能為空"); HttpPost httpPost = new HttpPost(httpUrl); List<NameValuePair> nvps = new ArrayList<NameValuePair>(); for(Iterator<Map.Entry<String, String>> iterator=request.entrySet().iterator(); iterator.hasNext();){ Map.Entry<String, String> entry = iterator.next(); nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } httpPost.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); System.out.println("Executing request: " + httpPost.getRequestLine()); CloseableHttpResponse response = httpClient.execute(httpPost); result = EntityUtils.toString(response.getEntity()); System.out.println("Executing response: "+ result); } catch (Exception e) { throw new RuntimeException(e); } finally { try { httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } return result; }
public static String httpPost(String json, String httpUrl, Map<String, String> headers){ String result = ""; CloseableHttpClient httpClient = getHttpClient(); try { if(StringUtils.isBlank(json)) throw new Exception("請求參數(shù)不能為空"); HttpPost httpPost = new HttpPost(httpUrl); for(Iterator<Map.Entry<String, String>> iterator=headers.entrySet().iterator();iterator.hasNext();){ Map.Entry<String, String> entry = iterator.next(); Header header = new BasicHeader(entry.getKey(), entry.getValue()); httpPost.setHeader(header); } httpPost.setEntity(new StringEntity(json, Charset.forName("UTF-8"))); System.out.println("Executing request: " + httpPost.getRequestLine()); CloseableHttpResponse response = httpClient.execute(httpPost); result = EntityUtils.toString(response.getEntity()); System.out.println("Executing response: "+ result); } catch (Exception e) { throw new RuntimeException(e); } finally { try { httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } return result; }
public static String httpGet(String httpUrl, Map<String, String> headers) { String result = ""; CloseableHttpClient httpClient = getHttpClient(); try { HttpGet httpGet = new HttpGet(httpUrl); System.out.println("Executing request: " + httpGet.getRequestLine()); for(Iterator<Map.Entry<String, String>> iterator=headers.entrySet().iterator();iterator.hasNext();){ Map.Entry<String, String> entry = iterator.next(); Header header = new BasicHeader(entry.getKey(), entry.getValue()); httpGet.setHeader(header); } CloseableHttpResponse response = httpClient.execute(httpGet); result = EntityUtils.toString(response.getEntity()); System.out.println("Executing response: "+ result); } catch (Exception e) { throw new RuntimeException(e); } finally { try { httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } return result; }
public static String httpGet(String httpUrl) { String result = ""; CloseableHttpClient httpClient = getHttpClient(); try { HttpGet httpGet = new HttpGet(httpUrl); System.out.println("Executing request: " + httpGet.getRequestLine()); CloseableHttpResponse response = httpClient.execute(httpGet); result = EntityUtils.toString(response.getEntity()); System.out.println("Executing response: "+ result); } catch (Exception e) { throw new RuntimeException(e); } finally { try { httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } return result; }
maven依賴: <!--httpclient--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.2</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> <version>4.4.4</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpmime</artifactId> <version>4.5.2</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.1</version> </dependency>
2015年11月3日
#
1、twemproxy explore 當我們有大量 Redis 或 Memcached 的時候,通常只能通過客戶端的一些數(shù)據(jù)分配算法(比如一致性哈希),來實現(xiàn)集群存儲的特性。雖然Redis 2.6版本已經(jīng)發(fā)布Redis Cluster,但還不是很成熟適用正式生產(chǎn)環(huán)境。 Redis 的 Cluster 方案還沒有正式推出之前,我們通過 Proxy 的方式來實現(xiàn)集群存儲。 Twitter,世界最大的Redis集群之一部署在Twitter用于為用戶提供時間軸數(shù)據(jù)。Twitter Open Source部門提供了Twemproxy。 Twemproxy,也叫nutcraker。是一個twtter開源的一個redis和memcache代理服務(wù)器。 redis作為一個高效的緩存服務(wù)器,非常具有應(yīng)用價值。但是當使用比較多的時候,就希望可以通過某種方式 統(tǒng)一進行管理。避免每個應(yīng)用每個客戶端管理連接的松散性。同時在一定程度上變得可以控制。 Twemproxy是一個快速的單線程代理程序,支持Memcached ASCII協(xié)議和更新的Redis協(xié)議: 它全部用C寫成,使用Apache 2.0 License授權(quán)。項目在Linux上可以工作,而在OSX上無法編譯,因為它依賴了epoll API. Twemproxy 通過引入一個代理層,可以將其后端的多臺 Redis 或 Memcached 實例進行統(tǒng)一管理與分配,使應(yīng)用程序只需要在 Twemproxy 上進行操作,而不用關(guān)心后面具體有多少個真實的 Redis 或 Memcached 存儲。 2、twemproxy特性: 另外可以修改redis的源代碼,抽取出redis中的前半部分,作為一個中間代理層。最終都是通過linux下的epoll 事件機制提高并發(fā)效率,其中nutcraker本身也是使用epoll的事件機制。并且在性能測試上的表現(xiàn)非常出色。 3、twemproxy問題與不足 Twemproxy 由于其自身原理限制,有一些不足之處,如: - 不支持針對多個值的操作,比如取sets的子交并補等(MGET 和 DEL 除外)
- 不支持Redis的事務(wù)操作
- 出錯提示還不夠完善
- 也不支持select操作
4、安裝與配置 Twemproxy 的安裝,主要命令如下: apt-get install automake apt-get install libtool git clone git://github.com/twitter/twemproxy.git cd twemproxy autoreconf -fvi ./configure --enable-debug=log make src/nutcracker -h 通過上面的命令就算安裝好了,然后是具體的配置,下面是一個典型的配置 redis1: listen: 127.0.0.1:6379 #使用哪個端口啟動Twemproxy redis: true #是否是Redis的proxy hash: fnv1a_64 #指定具體的hash函數(shù) distribution: ketama #具體的hash算法 auto_eject_hosts: true #是否在結(jié)點無法響應(yīng)的時候臨時摘除結(jié)點 timeout: 400 #超時時間(毫秒) server_retry_timeout: 2000 #重試的時間(毫秒) server_failure_limit: 1 #結(jié)點故障多少次就算摘除掉 servers: #下面表示所有的Redis節(jié)點(IP:端口號:權(quán)重) - 127.0.0.1:6380:1 - 127.0.0.1:6381:1 - 127.0.0.1:6382:1 redis2: listen: 0.0.0.0:10000 redis: true hash: fnv1a_64 distribution: ketama auto_eject_hosts: false timeout: 400 servers: - 127.0.0.1:6379:1 - 127.0.0.1:6380:1 - 127.0.0.1:6381:1 - 127.0.0.1:6382:1 你可以同時開啟多個 Twemproxy 實例,它們都可以進行讀寫,這樣你的應(yīng)用程序就可以完全避免所謂的單點故障。
http://blog.csdn.net/hguisu/article/details/9174459/
|