斷路器(curcuit breaker)模式
在分布式環(huán)境下,特別是微服務(wù)結(jié)構(gòu)的分布式系統(tǒng)中, 一個(gè)軟件系統(tǒng)調(diào)用另外一個(gè)遠(yuǎn)程系統(tǒng)是非常普遍的。這種遠(yuǎn)程調(diào)用的被調(diào)用方可能是另外一個(gè)進(jìn)程,或者是跨網(wǎng)路的另外一臺主機(jī), 這種遠(yuǎn)程的調(diào)用和進(jìn)程的內(nèi)部調(diào)用最大的區(qū)別是,遠(yuǎn)程調(diào)用可能會失敗,或者掛起而沒有任何回應(yīng),直到超時(shí)。更壞的情況是, 如果有多個(gè)調(diào)用者對同一個(gè)掛起的服務(wù)進(jìn)行調(diào)用,那么就很有可能的是一個(gè)服務(wù)的超時(shí)等待迅速蔓延到整個(gè)分布式系統(tǒng),引起連鎖反應(yīng), 從而消耗掉整個(gè)分布式系統(tǒng)大量資源。最終可能導(dǎo)致系統(tǒng)癱瘓。
斷路器(circuit breaker)模式就是為了防止在分布式系統(tǒng)中出現(xiàn)這種瀑布似的連鎖反應(yīng)導(dǎo)致的災(zāi)難。
一旦某個(gè)電器出問題,為了防止災(zāi)難,電路的保險(xiǎn)絲就會熔斷。斷路器類似于電路的保險(xiǎn)絲, 實(shí)現(xiàn)思路非常簡單,可以將需要保護(hù)的遠(yuǎn)程服務(wù)嗲用封裝起來,在內(nèi)部監(jiān)聽失敗次數(shù), 一旦失敗次數(shù)達(dá)到某閥值后,所有后續(xù)對該服務(wù)的調(diào)用,斷路器截獲后都直接返回錯(cuò)誤到調(diào)用方,而不會繼續(xù)調(diào)用已經(jīng)出問題的服務(wù), 從而達(dá)到保護(hù)調(diào)用方的目的, 整個(gè)系統(tǒng)也就不會出現(xiàn)因?yàn)槌瑫r(shí)而產(chǎn)生的瀑布式連鎖反應(yīng)。
1. 基本模式
上圖是斷路器(curcuit breaker)的結(jié)構(gòu),它有兩個(gè)基本狀態(tài)(close和open)和一個(gè)基本trip動作:
close狀態(tài)下, client向supplier發(fā)起的服務(wù)請求, 直接無阻礙通過斷路器, supplier的返回值接直接由斷路器交回給client.
open狀態(tài)下,client向supplier發(fā)起的服務(wù)請求后,斷路器不會將請求轉(zhuǎn)到supplier, 而是直接返回client, client和supplier之間的通路是斷的
trip: 在close狀態(tài)下,如果supplier持續(xù)超時(shí)報(bào)錯(cuò), 達(dá)到規(guī)定的閥值后,斷路器就發(fā)生trip, 之后斷路器狀態(tài)就會從close進(jìn)入open.
2. 擴(kuò)展模式
基本的斷路器模式下,保證了斷路器在open狀態(tài)時(shí),保護(hù)supplier不會被調(diào)用, 但我們還需要額外的措施可以在supplier恢復(fù)服務(wù)后,可以重置斷路器。一種可行的辦法是斷路器定期探測supplier的服務(wù)是否恢復(fù), 一但恢復(fù), 就將狀態(tài)設(shè)置成close。斷路器進(jìn)行重試時(shí)的狀態(tài)為半開(half-open)狀態(tài)。
3. 斷路器的使用場合:
一個(gè)supplier一般很穩(wěn)定,如果一旦故障發(fā)生后, 檢查和恢復(fù)需要的時(shí)間比較長,通常無法短時(shí)間內(nèi)快速修復(fù)的,那么這種服務(wù)比較適合采用斷路器模式。否則很可能導(dǎo)致ping-pong效應(yīng)。
3. 斷路器不適合的場合:
為了防止一個(gè)應(yīng)用程序試圖調(diào)用一個(gè)遠(yuǎn)程服務(wù)或訪問共享資源,如果??該操作是極有可能失敗, 這種模式可能不適合。
對于處理中的應(yīng)用程序訪問本地專用資源,例如在存儲器內(nèi)數(shù)據(jù)結(jié)構(gòu)。在這種環(huán)境下通常也不適合,使用斷路器只會增加系統(tǒng)開銷。
下面直接介紹spring cloud的斷路器如何使用。
springcloud netflix實(shí)現(xiàn)了斷路器庫的名字叫hystrix. 在微服務(wù)架構(gòu)下,通常會有多個(gè)層次的服務(wù)調(diào)用.下面是微服架構(gòu)下, 瀏覽器端通過api訪問后臺微服務(wù)的一個(gè)示意圖:
一個(gè)微服務(wù)的超時(shí)失敗可能導(dǎo)致瀑布式連鎖反映,下圖中,hystrix通過自主反饋實(shí)現(xiàn)的斷路器,防止了這種情況發(fā)生。
圖中的服務(wù)b因?yàn)槟承┰蚴。兊貌豢捎茫袑Ψ?wù)b的調(diào)用都會超時(shí)。當(dāng)對b的調(diào)用失敗達(dá)到一個(gè)特定的閥值(5秒之內(nèi)發(fā)生20次失敗是hystrix定義的缺省值), 鏈路就會被處于open狀態(tài), 之后所有所有對服務(wù)b的調(diào)用都不會被執(zhí)行, 取而代之的是由斷路器提供的一個(gè)表示鏈路open的fallback消息. hystrix提供了相應(yīng)機(jī)制,可以讓開發(fā)者定義這個(gè)fallbak消息.
open的鏈路阻斷了瀑布式錯(cuò)誤, 可以讓被淹沒或者錯(cuò)誤的服務(wù)有時(shí)間進(jìn)行修復(fù)。這個(gè)fallback可以是另外一個(gè)hystrix保護(hù)的調(diào)用, 靜態(tài)數(shù)據(jù),或者合法的空值. fallbacks可以組成鏈?zhǔn)浇Y(jié)構(gòu),所以,最底層調(diào)用其它業(yè)務(wù)服務(wù)的第一個(gè)fallback返回靜態(tài)數(shù)據(jù).
下面,進(jìn)入正題,在之前的兩hello world服務(wù)集群中加入斷路器, 防止其中一個(gè)hello world掛掉后, 導(dǎo)致系統(tǒng)發(fā)生連鎖超時(shí)失敗。
1. 在maven工程(前面章節(jié)中介紹的ribbon或者feign工程)的pom.xml中添加hystrix庫支持?jǐn)嗦菲?/p>
1
2
3
4
|
<dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-hystrix</artifactid> </dependency> |
2.在ribbon應(yīng)用中使用斷路器
1). 在spring boot啟動類上添加@enablecircuitbreaker注解
1
2
3
4
5
6
7
8
|
@springbootapplication @enablediscoveryclient @enablecircuitbreaker public class serviceribbonapplication { public static void main(string[] args) { springapplication.run(serviceribbonapplication. class , args); } |
2). 用@hystrixcommand注解標(biāo)注訪問服務(wù)的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@service public class helloservice { @autowired resttemplate resttemplate; @hystrixcommand (fallbackmethod = "servicefailure" ) public string gethellocontent() { return resttemplate.getforobject( "http://service-helloworld/" ,string. class ); } public string servicefailure() { return "hello world service is not available !" ; } } |
@hystrixcommand注解定義了一個(gè)斷路器,它封裝了gethellocontant()方法, 當(dāng)它訪問的service-helloworld失敗達(dá)到閥值后,將不會再調(diào)用service-helloworld, 取而代之的是返回由fallbackmethod定義的方法servicefailure()。@hystrixcommand注解定義的fallbackmethod方法,需要特別注意的有兩點(diǎn):
第一, fallbackmethod的返回值和參數(shù)類型需要和被@hystrixcommand注解的方法完全一致。否則會在運(yùn)行時(shí)拋出異常。比如本例中,servicefailure()的返回值和gethellocontant()方法的返回值都是string。
第二, 當(dāng)?shù)讓臃?wù)失敗后,fallbackmethod替換的不是整個(gè)被@hystrixcommand注解的方法(本例中的gethellocontant), 替換的只是通過resttemplate去訪問的具體服務(wù)。可以從中的system輸出看到, 即使失敗,控制臺輸出里面依然會有“call service-helloworld”。
啟動eureka服務(wù),只啟動兩個(gè)helloworld服務(wù),然后中斷其中一個(gè)(模擬其中一個(gè)微服務(wù)掛起),訪問http://localhost:8901/然后刷新, 由于有負(fù)載均衡可以看到以下兩個(gè)頁面交替出現(xiàn)。可以看到第二個(gè)被掛起的服務(wù),被定義在ribbon應(yīng)該里面的錯(cuò)誤處理方法替換了。
4. 在feign應(yīng)用中使用斷路器
1). feign內(nèi)部已經(jīng)支持了斷路器,所以不需要想ribbon方式一樣,在spring boot啟動類上加額外注解
2). 用@feignclient注解添加fallback類, 該類必須實(shí)現(xiàn)@feignclient修飾的接口。
1
2
3
4
5
|
@feignclient (name = "service-helloworld" , fallback = helloworldservicefailure. class ) public interface helloworldservice { @requestmapping (value = "/" , method = requestmethod.get) public string sayhello(); } |
3). 創(chuàng)建helloworldservicefailure類, 必須實(shí)現(xiàn)被@feignclient修飾的helloworldservice接口。注意添加@component或者@service注解,在spring容器中生成一個(gè)bean
1
2
3
4
5
6
7
8
|
@component public class helloworldservicefailure implements helloworldservice { @override public string sayhello() { system.out.println( "hello world service is not available !" ); return "hello world service is not available !" ; } } |
4). spring cloud之前的brixton版本中,feign是缺省是自動激活了斷路器的,但最近的dalston版本已經(jīng)將缺省配置修改為禁止。
原因參見: https://github.com/spring-cloud/spring-cloud-netflix/issues/1277, 這一點(diǎn)要注意。所以要在feign中使用斷路器, 必須在application.yml中添加如下配置:
1
2
3
|
feign: hystrix: enabled: true |
5). 啟動feign應(yīng)用, 訪問http://localhost:8902/hello, 可以一看到和ribbon一樣的效果。
參考:http://projects.spring.io/spring-cloud/spring-cloud.html#_circuit_breaker_hystrix_clients
http://projects.spring.io/spring-cloud/spring-cloud.html#spring-cloud-feign-hystrix
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://www.cnblogs.com/chry/p/7279856.html