小編給大家分享一下hystrix-go怎么用,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!
在東勝等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供成都網(wǎng)站建設(shè)、網(wǎng)站建設(shè) 網(wǎng)站設(shè)計制作按需求定制開發(fā),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),成都營銷網(wǎng)站建設(shè),外貿(mào)網(wǎng)站建設(shè),東勝網(wǎng)站建設(shè)費用合理。
開篇
這周在看內(nèi)部一個熔斷限流包時,發(fā)現(xiàn)它是基于一個開源項目 hystrix-go
實現(xiàn)了,因此有了這篇文章。
Hystrix
是由 Netflex
開發(fā)的一款開源組件,提供了基礎(chǔ)的熔斷功能。 Hystrix
將降級的策略封裝在 Command
中,提供了 run
和 fallback
兩個方法,前者表示正常的邏輯,比如微服務(wù)之間的調(diào)用……,如果發(fā)生了故障,再執(zhí)行 fallback
方法返回結(jié)果,我們可以把它理解成保底操作。如果正常邏輯在短時間內(nèi)頻繁發(fā)生故障,那么可能會觸發(fā)短路,也就是之后的請求不再執(zhí)行 run
,而是直接執(zhí)行 fallback
。更多關(guān)于 Hystrix
的信息可以查看 https://github.com/Netflix/Hystrix
,而hystrix-go
則是用 go
實現(xiàn)的 hystrix
版,更確切的說,是簡化版。只是上一次更新還是 2018年 的一次 pr
,也就畢業(yè)了?
為什么需要這些工具?
比如一個微服務(wù)化的產(chǎn)品線上,每一個服務(wù)都專注于自己的業(yè)務(wù),并對外提供相應(yīng)的服務(wù)接口,或者依賴于外部服務(wù)的某個邏輯接口,就像下面這樣。
假設(shè)我們當(dāng)前是 服務(wù)A
,有部分邏輯依賴于 服務(wù)C
,服務(wù)C
又依賴于 服務(wù)E
,當(dāng)前微服務(wù)之間進行 rpc
或者 http
通信,假設(shè)此時 服務(wù)C
調(diào)用 服務(wù)E 失敗,比如由于網(wǎng)絡(luò)波動導(dǎo)致超時或者服務(wù)E由于過載,系統(tǒng)E 已經(jīng)down掉了。
調(diào)用失敗,一般會有失敗重試等機制。但是再想想,假設(shè)服務(wù)E已然不可用的情況下,此時新的調(diào)用不斷產(chǎn)生,同時伴隨著調(diào)用等待和失敗重試,會導(dǎo)致 服務(wù)C對服務(wù)E的調(diào)用而產(chǎn)生大量的積壓,慢慢會耗盡服務(wù)C的資源,進而導(dǎo)致服務(wù)C也down掉,這樣惡性循環(huán)下,會影響到整個微服務(wù)體系,產(chǎn)生雪崩效應(yīng)。
雖然導(dǎo)致雪崩的發(fā)生不僅僅這一種,但是我們需要采取一定的措施,來保證不讓這個噩夢發(fā)生。而 hystrix-go
就很好的提供了 熔斷和降級的措施。它的主要思想在于,設(shè)置一些閥值,比如最大并發(fā)數(shù)(當(dāng)并發(fā)數(shù)大于設(shè)置的并發(fā)數(shù),攔截),錯誤率百分比(請求數(shù)量大于等于設(shè)置 的閥值,并且錯誤率達(dá)到設(shè)置的百分比時,觸發(fā)熔斷)以及熔斷嘗試恢復(fù)時間等 。
hystrix-go
的使用非常簡單,你可以調(diào)用它的 Go
或者 Do
方法,只是 Go
方法是異步的方式。而 Do
方法是同步方式。我們從一個簡單的例子開啟。
_ = hystrix.Do("wuqq", func() error { // talk to other services _, err := http.Get("https://www.baidu.com/") if err != nil { fmt.Println("get error:%v",err) return err } return nil }, func(err error) error { fmt.Printf("handle error:%v\n", err) return nil })
Do
函數(shù)需要三個參數(shù),第一個參數(shù) commmand
名稱,你可以把每個名稱當(dāng)成一個獨立當(dāng)服務(wù),第二個參數(shù)是處理正常的邏輯,比如 http
調(diào)用服務(wù),返回參數(shù)是 err
。如果處理|調(diào)用失敗,那么就執(zhí)行第三個參數(shù)邏輯, 我們稱為保底操作。由于服務(wù)錯誤率過高導(dǎo)致熔斷器開啟,那么之后的請求也直接回調(diào)此函數(shù)。
既然熔斷器是按照配置的規(guī)則而進行是否開啟的操作,那么我們當(dāng)然可以設(shè)置我們想要的值。
hystrix.ConfigureCommand("wuqq", hystrix.CommandConfig{ Timeout: int(3 * time.Second), MaxConcurrentRequests: 10, SleepWindow: 5000, RequestVolumeThreshold: 10, ErrorPercentThreshold: 30, }) _ = hystrix.Do("wuqq", func() error { // talk to other services _, err := http.Get("https://www.baidu.com/") if err != nil { fmt.Println("get error:%v",err) return err } return nil }, func(err error) error { fmt.Printf("handle error:%v\n", err) return nil })
稍微解釋一下上面配置的值含義:
Timeout: 執(zhí)行 command
的超時時間。
MaxConcurrentRequests:command
的最大并發(fā)量 。
SleepWindow:當(dāng)熔斷器被打開后,SleepWindow
的時間就是控制過多久后去嘗試服務(wù)是否可用了。
RequestVolumeThreshold: 一個統(tǒng)計窗口10秒內(nèi)請求數(shù)量。達(dá)到這個請求數(shù)量后才去判斷是否要開啟熔斷
ErrorPercentThreshold:錯誤百分比,請求數(shù)量大于等于RequestVolumeThreshold
并且錯誤率到達(dá)這個百分比后就會啟動熔斷
當(dāng)然你不設(shè)置的話,那么自動走的默認(rèn)值。
我們再來看一個簡單的例子:
package mainimport ( "fmt" "github.com/afex/hystrix-go/hystrix" "net/http" "time")type Handle struct{}func (h *Handle) ServeHTTP(r http.ResponseWriter, request *http.Request) { h.Common(r, request)}func (h *Handle) Common(r http.ResponseWriter, request *http.Request) { hystrix.ConfigureCommand("mycommand", hystrix.CommandConfig{ Timeout: int(3 * time.Second), MaxConcurrentRequests: 10, SleepWindow: 5000, RequestVolumeThreshold: 20, ErrorPercentThreshold: 30, }) msg := "success" _ = hystrix.Do("mycommand", func() error { _, err := http.Get("https://www.baidu.com") if err != nil { fmt.Printf("請求失敗:%v", err) return err } return nil }, func(err error) error { fmt.Printf("handle error:%v\n", err) msg = "error" return nil }) r.Write([]byte(msg))}func main() { http.ListenAndServe(":8090", &Handle{})}
我們開啟了一個 http
服務(wù),監(jiān)聽端口號 8090
,所有請求的處理邏輯都在 Common
方法中,在這個方法中,我們主要是發(fā)起一次 http
請求,請求成功響應(yīng)success
,如果失敗,響應(yīng)失敗原因。
我們再寫另一個簡單程序,并發(fā) 11
次的請求 8090
端口。
package mainimport ( "fmt" "io/ioutil" "net/http" "sync" "time")var client *http.Clientfunc init() { tr := &http.Transport{ MaxIdleConns: 100, IdleConnTimeout: 1 * time.Second, } client = &http.Client{Transport: tr}}type info struct { Data interface{} `json:"data"`}func main() { var wg sync.WaitGroup for i := 0; i < 11; i++ { wg.Add(1) go func(int2 int) { defer wg.Done() req, err := http.NewRequest("GET", "http://localhost:8090", nil) if err != nil { fmt.Printf("初始化http客戶端處錯誤:%v", err) return } resp, err := client.Do(req) if err != nil { fmt.Printf("初始化http客戶端處錯誤:%v", err) return } defer resp.Body.Close() nByte, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Printf("讀取http數(shù)據(jù)失敗:%v", err) return } fmt.Printf("接收到到值:%v\n", string(nByte)) }(i) } wg.Wait() fmt.Printf("請求完畢\n")}
由于我們配置 MaxConcurrentRequests
為10,那么意味著還有個 g 請求會失敗:
和我們想的一樣。
接著我們把網(wǎng)絡(luò)斷開,并發(fā)請求改成10次。再次運行程序并發(fā)請求 8090
端口,此時由于網(wǎng)絡(luò)已關(guān)閉,導(dǎo)致請求百度失?。?br/>
接著繼續(xù)請求:
熔斷器已開啟,上面我們配置的RequestVolumeThreshold
和 ErrorPercentThreshold
生效。
然后我們把網(wǎng)連上,五秒后 (SleepWindow
的值)繼續(xù)并發(fā)調(diào)用,當(dāng)前熔斷器處于半開的狀態(tài),此時請求允許調(diào)用依賴,如果成功則關(guān)閉,失敗則繼續(xù)開啟熔斷器。
可以看到,有一個成功了,那么此時熔斷器已關(guān)閉,接下來繼續(xù)運行函數(shù)并發(fā)調(diào)用:
可以看到,10個都已經(jīng)是正常成功的狀態(tài)了。
那么問題來了,為什么最上面的圖只有一個是成功的?5秒已經(jīng)過了,并且當(dāng)前網(wǎng)絡(luò)正常,應(yīng)該是10個請求都成功,但是我們看到的只有一個是成功狀態(tài)。通過源碼我們可以找到答案:
具體邏輯在判斷當(dāng)前請求是否可以調(diào)用依賴
if !cmd.circuit.AllowRequest() { ...... return }
func (circuit *CircuitBreaker) AllowRequest() bool { return !circuit.IsOpen() || circuit.allowSingleTest()}func (circuit *CircuitBreaker) allowSingleTest() bool { circuit.mutex.RLock() defer circuit.mutex.RUnlock() now := time.Now().UnixNano() openedOrLastTestedTime := atomic.LoadInt64(&circuit.openedOrLastTestedTime) if circuit.open && now > openedOrLastTestedTime+getSettings(circuit.Name).SleepWindow.Nanoseconds() { / swapped := atomic.CompareAndSwapInt64(&circuit.openedOrLastTestedTime, openedOrLastTestedTime, now) //這一句才是關(guān)鍵 if swapped { log.Printf("hystrix-go: allowing single test to possibly close circuit %v", circuit.Name) } return swapped } return false}
這段代碼首先判斷了熔斷器是否開啟,并且當(dāng)前時間大于 上一次開啟熔斷器的時間+ SleepWindow
的時間,如果條件都符合的話,更新此熔斷器最新的 openedOrLastTestedTime
,是通過 CompareAndSwapInt64
原子操作完成的,意外著必然只會有一個成功。
此時熔斷器還是半開的狀態(tài),接著如果能拿到令牌,執(zhí)行run
函數(shù)(也就是Do傳入的第二個簡單封裝后的函數(shù)),發(fā)起 http
請求,如果成功,上報成功狀態(tài),關(guān)閉熔斷器。如果失敗,那么熔斷器依舊開啟。
看完了這篇文章,相信你對“hystrix-go怎么用”有了一定的了解,如果想了解更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!