簡介
在分布式環(huán)境中部逮,許多服務依賴關系中的一些必然會失敗。Hystrix是一個庫,它通過添加延遲容忍和容錯邏輯來幫助您控制這些分布式服務之間的交互藏澳。Hystrix通過隔離服務之間的訪問點、停止跨服務的級聯(lián)故障并提供回退選項來實現(xiàn)這一點耀找,所有這些選項都提高了系統(tǒng)的總體彈性翔悠。
目標
Hystrix的設計目的如下:
- 為通過第三方客戶端庫訪問的依賴項(通常通過網(wǎng)絡)提供保護和控制延遲和故障。
- 停止復雜分布式系統(tǒng)中的級聯(lián)故障野芒。
- 故障快速恢復蓄愁。
- 在可能的情況下,后退并優(yōu)雅地降級狞悲。
- 啟用近實時監(jiān)視撮抓、警報和操作控制。
背景
為了解決什么問題摇锋?
復雜分布式體系結構中的應用程序有幾十個依賴項丹拯,每個依賴項在某個時候都不可避免地會失敗。如果主機應用程序沒有從這些外部故障中隔離出來荸恕,那么它就有可能與這些外部故障一起宕機乖酬。
例如,對于一個依賴于30個服務的應用程序融求,其中每個服務都有99.99%的正常運行時間咬像,您可以這樣期望:
99.9930 = 99.7% uptime
0.3% of 1 billion requests = 3,000,000 failures
2+ hours downtime/month even if all dependencies have excellent uptime.
現(xiàn)實通常更糟。
即使當所有依賴項都運行良好時双肤,即使0.01%的停機時間對幾十個服務中的每個服務的總體影響也相當于一個月潛在的停機時間(如果您不為恢復而設計整個系統(tǒng))施掏。
如下面的圖演變:
當一切正常時,請求流可以是這樣的:
當許多后端系統(tǒng)之一成為潛在茅糜,它可以阻止整個用戶請求:
對于高流量七芭,一個后端依賴項成為潛在,可能會導致所有服務器上的所有資源在幾秒鐘內(nèi)飽和蔑赘。
應用程序中通過網(wǎng)絡或客戶機庫到達可能導致網(wǎng)絡請求的每個點都是潛在故障的來源狸驳。比故障更糟的是,這些應用程序還可能導致服務之間的延遲增加缩赛,從而備份隊列耙箍、線程和其他系統(tǒng)資源,從而導致系統(tǒng)中出現(xiàn)更多級聯(lián)故障酥馍。
工作原理
工作流程圖:
1. 構造一個HystrixCommand或HystrixObservableCommand對象
第一步是構造一個HystrixCommand或HystrixObservableCommand對象來表示對依賴項的請求辩昆。將請求發(fā)出時需要的任何參數(shù)傳遞給構造函數(shù)。
如果期望依賴項返回單個響應旨袒,則構造一個HystrixCommand對象汁针。例如:
HystrixCommand command = new HystrixCommand(arg1, arg2);
如果期望依賴項返回發(fā)出響應的可觀察對象术辐,則構造一個HystrixObservableCommand對象。例如:
HystrixObservableCommand command = new HystrixObservableCommand(arg1, arg2);
2.執(zhí)行命令
有四種方法可以執(zhí)行命令施无,使用以下四種方法之一的Hystrix命令對象(前兩種方法只適用于簡單的HystrixCommand對象辉词,不適用于HystrixObservableCommand):
-
execute()
— blocks, then returns the single response received from the dependency (or throws an exception in case of an error) -
queue()
— returns aFuture
with which you can obtain the single response from the dependency -
observe()
— subscribes to theObservable
that represents the response(s) from the dependency and returns anObservable
that replicates that sourceObservable
-
toObservable()
— returns anObservable
that, when you subscribe to it, will execute the Hystrix command and emit its responses
3.是否緩存了響應
如果為該命令啟用了請求緩存,并且在緩存中可用對請求的響應猾骡,則此緩存的響應將立即以可觀察到的形式返回瑞躺。
4. 電路打開了嗎?
當您執(zhí)行該命令時,Hystrix將與斷路器一起檢查電路是否打開兴想。
如果電路打開(或“跳閘”)幢哨,那么Hystrix將不執(zhí)行命令,而是將流路由到(8)獲取回退嫂便。
如果電路被關閉嘱么,則流繼續(xù)到(5),檢查是否有可用的容量來運行命令顽悼。
5.線程池/隊列/信號量是否已滿?
如果與該命令關聯(lián)的線程池和隊列(或信號量,如果不在線程中運行)已滿几迄,那么Hystrix將不執(zhí)行該命令蔚龙,而是立即將流路由到(8)獲取回退。
6.HystrixObservableCommand.construct()或HystrixCommand.run ()
這里映胁,Hystrix通過為此目的編寫的方法調(diào)用對依賴項的請求木羹,方法如下:
-
HystrixCommand.run()
— returns a single response or throws an exception -
HystrixObservableCommand.construct()
— returns an Observable that emits the response(s) or sends anonError
notification
如果run()或construct()方法超過了命令的超時值,線程將拋出一個TimeoutException(如果命令本身不在自己的線程中運行解孙,則單獨的計時器線程將拋出一個TimeoutException)坑填。在這種情況下,Hystrix將響應路由到8弛姜。獲取回退脐瑰,如果最終返回值run()或construct()方法沒有取消/中斷,那么它將丟棄該方法廷臼。
請注意苍在,沒有辦法強制潛在線程停止工作——Hystrix在JVM上能做的最好的事情就是拋出InterruptedException。如果由Hystrix包裝的工作不尊重interruptedexception荠商,那么Hystrix線程池中的線程將繼續(xù)它的工作寂恬,盡管客戶機已經(jīng)收到了TimeoutException。這種行為可能會使Hystrix線程池飽和莱没,盡管負載“正確釋放”初肉。大多數(shù)Java HTTP客戶端庫不解釋interruptedexception。因此饰躲,請確保正確配置HTTP客戶機上的連接和讀/寫超時牙咏。
如果該命令沒有拋出任何異常并返回一個響應臼隔,那么Hystrix將在執(zhí)行一些日志記錄和度量報告之后返回此響應。在run()的情況下眠寿,Hystrix返回一個可觀察的對象躬翁,該對象發(fā)出單個響應,然后發(fā)出一個onCompleted通知;在construct()的情況下盯拱,Hystrix返回由construct()返回的相同的可觀察值盒发。
7.計算電路健康
Hystrix向斷路器報告成功、失敗狡逢、拒絕和超時宁舰,斷路器維護一組滾動計數(shù)器,用于計算統(tǒng)計數(shù)據(jù)奢浑。
它使用這些統(tǒng)計數(shù)據(jù)來確定電路應該在什么時候“跳閘”蛮艰,在這一點上,它會短路任何后續(xù)的請求雀彼,直到恢復期結束壤蚜,在此期間,它會在第一次檢查某些健康檢查之后再次關閉電路徊哑。
8.回退
Hystrix試圖恢復你的回滾命令執(zhí)行失敗時:當一個異常的構造()或()運行(6),當命令電路短路,因為打開(4),當命令的線程池和隊列或信號能力(5),或者當命令已超過其超時長度袜刷。
詳情參考官網(wǎng):https://github.com/Netflix/Hystrix/wiki/How-it-Works#8-get-the-fallback
9. 返回成功的響應
如果Hystrix命令成功,它將以可觀察到的形式返回響應或響應給調(diào)用者莺丑。根據(jù)您如何調(diào)用上面步驟2中的命令著蟹,這個可觀察對象可能在返回給您之前進行轉換:
- execute() — 以與.queue()相同的方式獲取一個Future,然后在這個Future上調(diào)用get()來獲取可觀察對象發(fā)出的單個值.
- queue() — 將可觀察對象轉換為BlockingObservable梢莽,以便將其轉換為未來萧豆,然后返回此未來
- observe() — 立即訂閱可觀察對象,并開始執(zhí)行命令的流;返回一個可觀察對象昏名,當您訂閱該對象時涮雷,將重播排放和通知
- toObservable() — 返回可觀察值不變;您必須訂閱它,才能真正開始執(zhí)行命令的流程
更多原理可以移步官網(wǎng)
https://github.com/Netflix/Hystrix/wiki/How-it-Works
使用
加入依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
在ribbon中使用
使用@EnableHystrix開啟
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableHystrix
public class CloudServiceRibbonApplication {
public static void main(String[] args) {
SpringApplication.run(CloudServiceRibbonApplication.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
該注解對該方法創(chuàng)建了熔斷器的功能轻局,并指定了fallbackMethod熔斷方法份殿,熔斷方法直接返回了一個字符串,字符串為"hi,"+name+",sorry,error!"
@Service
public class TestService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "hiError")
public String hiService(String name) {
return restTemplate.getForObject("http://CLOUD-EUREKA-CLIENT/hi?name="+name,String.class);
}
public String hiError(String name) {
return "hi,"+name+",sorry,error!";
}
}
在Feign中使用
feign.hystrix.enabled: true 開啟hystrix
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8765
spring:
application:
name: cloud-service-feign
feign.hystrix.enabled: true
@EnableFeignClients啟動
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableFeignClients
public class CloudServiceFeginApplication {
public static void main(String[] args) {
SpringApplication.run(CloudServiceFeginApplication.class, args);
}
}
fallback:配置連接失敗等錯誤的返回類
@FeignClient(value = "cloud-eureka-client",fallback = TestServiceHystric.class)
public interface TestService {
@RequestMapping(value = "/hi",method = RequestMethod.GET)
String sayHiFromClientOne(@RequestParam(value = "name") String name);
}
當訪問接口有問題時嗽交,直接調(diào)用此接口返回卿嘲。
@Component
public class TestServiceHystric implements TestService{
@Override
public String sayHiFromClientOne(String name) {
return "sorry "+name;
}
}
更多使用技巧可參考官網(wǎng):
https://github.com/Netflix/Hystrix/wiki/How-To-Use
總結
在微服務架構中通常會有多個服務層調(diào)用,基礎服務的故障可能會導致級聯(lián)故障夫壁,進而造成整個系統(tǒng)不可用的情況拾枣,這種現(xiàn)象被稱為服務雪崩效應。服務雪崩效應是一種因“服務提供者”的不可用導致“服務消費者”的不可用,并將不可用逐漸放大的過程。
熔斷器的原理很簡單梅肤,如同電力過載保護器司蔬。它可以實現(xiàn)快速失敗,如果它在一段時間內(nèi)偵測到許多類似的錯誤姨蝴,會強迫其以后的多個調(diào)用快速失敗俊啼,不再訪問遠程服務器,從而防止應用程序不斷地嘗試執(zhí)行可能會失敗的操作左医,使得應用程序繼續(xù)執(zhí)行而不用等待修正錯誤授帕,或者浪費CPU時間去等到長時間的超時產(chǎn)生。熔斷器也可以使應用程序能夠診斷錯誤是否已經(jīng)修正浮梢,如果已經(jīng)修正跛十,應用程序會再次嘗試調(diào)用操作。
更多優(yōu)質文章:
最后
如果對 Java秕硝、大數(shù)據(jù)感興趣請長按二維碼關注一波芥映,我會努力帶給你們價值。覺得對你哪怕有一丁點幫助的請幫忙點個贊或者轉發(fā)哦远豺。