1、簡介Hystrix
說實話,憑借hystrix的頂頂大名,關於它的簡介到處都是。最准確的當然還是官方的,有興趣的可以看一下它官方的wiki:https://github.com/Netflix/Hystrix/wiki
In a distributed environment, inevitably some of the many service dependencies will fail. Hystrix is a library that helps you control the interactions between these distributed services by adding latency tolerance and fault tolerance logic. Hystrix does this by isolating points of access between the services, stopping cascading failures across them, and providing fallback options, all of which improve your system’s overall resiliency. |
簡單來說就是增強一個分布式系統的容錯率和錯誤處理邏輯。hystrix通過對依賴失敗進行隔離,將整個服務的延遲都縮小到一個可控的范圍內。也就是說整個服務的響應時間是可控的,不會因為某個依賴服務的延遲或錯誤,或者是因為高並發而使整個服務的響應時間大大增加甚至是服務雪崩。
2、Hystrix如何解決依賴隔離
Hystrix其實一套command pattern(命令模式)的封裝,要明白Hystrix的工作原理首先要明白command pattern ,關於command pattern 可以看看這:http://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/command.html
一般命令模式的UML圖:
Hystrix有點不同(部分UML)
它提供了抽象命令類,並將invoker和abstract合並到一起,並在command內提供了調用的命令的具體參數,它的總共提供了三種執行模式,分別是execute(同步模式),queue(異步執行),Reactive模式(Observable(立即請求),toObservable(訂閱時請求))。
- 將所有請求外部系統(或者叫依賴服務)的邏輯封裝到 HystrixCommand 或者 HystrixObservableCommand 對象中,這些邏輯將會在獨立的線程中被執行
- Hystrix 采取自動超時的策略(默認1000ms)。該策略默認對所有 Command 都有效,也可以通過設置 Command 的配置以自定義超時時間,所有的默認設置在HystrixCommandProperties中看到。
- 可以對每個服務維護一個線程池,可以選擇當線程用完時是等待還是拒絕,等待的最大隊列長度是多少,這個大大的提高了對依賴服務可控性和靈活性,所有的關於線程池的配置都可以在HystrixThreadPoolProperties中看到
- 分出成功、失敗(拋出異常)、超時或者線程池占滿四種請求依賴服務時可能出現的異常,所有的異常都可以通過getExecutionException()這個默認方法進行獲取
- 引入『熔斷器』機制,在依賴服務失效比例超過閾值時,手動或者自動地切斷服務一段時間
- 請求失敗執行fallback()方法,多個請求(繼承HystrixCollapser)當其中一個失敗,后面的請求都不會執行,而直接執行fallback
- Hystrix提供監控和配置變更
3、Hystrix執行
- 構建HystrixCommand或者HystrixObservableCommand對象;
- 執行命令(execute()、queue()、observe()、toObservable());
- 如果請求結果緩存這個特性被啟用,並且緩存命中,則緩存的回應會立即通過一個Observable對象的形式返回;
- 檢查熔斷器狀態,確定請求線路是否是開路,如果請求線路是開路,Hystrix將不會執行這個命令,而是直接使用『失敗回退邏輯』(即不會執行run(),直接執行getFallback());
- 如果和當前需要執行的命令相關聯的線程池和請求隊列(或者信號量,如果不使用線程池)滿了,Hystrix 將不會執行這個命令,而是直接使用『失敗回退邏輯』(即不會執行run(),直接執行getFallback());
- 執行HystrixCommand.run()或HystrixObservableCommand.construct(),如果這兩個方法執行超時或者執行失敗,則執行getFallback();如果正常結束,Hystrix 在添加一些日志和監控數據采集之后,直接返回回應;
- Hystrix 會將請求成功,失敗,被拒絕或超時信息報告給熔斷器,熔斷器維護一些用於統計數據用的計數器。
4、hello world
@Log4j public class DemoHystrix extends HystrixCommand<Boolean>{ private DemoService demoService; private Demo demo; protected DemoHystrix(DemoService demoService,Demo demo) { super((Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("RemoteServiceX")) .andThreadPoolPropertiesDefaults(com.netflix.hystrix.HystrixThreadPoolProperties.Setter() .withCoreSize(10) .withMaximumSize(100)
//線程飽和后拒絕所有服務 .withMaxQueueSize(-1)
//使MaximumSize生效 .withAllowMaximumSizeToDivergeFromCoreSize(true)))
//fallback和execute用的同一個線程,這個配置可以使fallback不搶占execute的線程 .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("RemoteServiceXFallback"))); this.demoService = demoService; this.demo = demo; } @Override protected Boolean run() throws Exception { return demoService.queryDemoById(29)!=null; } @Override protected Boolean getFallback() {
//可以根據不同的異常進行處理 System.out.println("~~~~~~~~~~~~~~~~~this is fallback "+getExecutionException().getMessage()); return false; } }
這是一般脫離框架的模式(沒用spring),Hystrix設計的是一個command實例只能執行一次,如果
DemoHystrix demoHystrix = new DemoHystrix(demoService,demo); demoHystrix.execute(); demoHystrix.execute();
會直接拋異常,所以這也就理解了為什么每個Command都必須要有個有個CommandKey,這樣同一個command都在相同的線程池里執行,甚至是可以定義出一個服務組的概念。
5、hystrix在spring mvc的使用
通過上面的方式可以看出,hystrix並不依賴任何框架,它只是一個命令模式的集合,你甚至可以不把它用在服務治理上,把它用在任何你需要的場景上都是可以的,但是因為歷史遺留的問題,如果要使用hystrix而不用改動太多代碼的話,hystrix提供了hystrix-javanica包
使用maven引入hystrix依賴: <dependency> <groupId>com.netflix.hystrix</groupId> <artifactId>hystrix-javanica</artifactId> <version>1.5.12</version> </dependency>
少不了的spring的配置。。。
@Configuration @Import(com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect.class) @EnableAspectJAutoProxy public class AppConfig extends WebMvcConfigurerAdapter { }
配置很簡單,因為hystrix已經做好了,就是做了一個aop,對所有執行帶有@HystrixCommand的方法做了切面,然后包裝成
HystrixInvokable執行。
然后你就可以直接的使用hystrix了
@Component @Slf4j public class WrapService { @Reference(version = "1.0.0") private DemoService demoService; @Reference(version = "1.0.0") private OtherService otherService; @Reference(version = "2.0.0") private OtherService otherService2; public boolean demoServiceInsert(Demo demo){ DemoHystrix demoHystrix = new DemoHystrix(demoService,demo); return demoHystrix.execute(); } @HystrixCommand(groupKey = "DemoCommand", fallbackMethod = "getDemoBack", threadPoolProperties={@HystrixProperty(name = "coreSize",value = "10"), @HystrixProperty(name = "maximumSize",value = "100"), @HystrixProperty(name = "allowMaximumSizeToDivergeFromCoreSize",value = "true")}) public Demo getDemo(){ return demoService.queryDemoById(29); } private Demo getDemoBack(){ log.info("getDemoBack"); return new Demo(); } public OtherService getOtherService(){ return otherService; } public OtherService getOtherService2(){ return otherService2; } }
和剛才的服務一樣,只是在這種情況下在fallback中就不能獲得異常了,其他所有的配置都可以在注解中完成。
6、總結
Hystrix會損失一定性能在一定程度上,根據官方的文檔如果設置的好的話大概能到原來的99.5%,但是這帶來了對依賴服務極大的控制力和靈活度,它本身的使用很簡單,不需要太多的學習成本,更可貴的是,對於以前的老系統是從同步方式轉到異步或者是reactive模式提供了一個可行的方法,這是非常了不起的。
監控忘了說了,但它本身很簡單,對Hystrix有興趣的可以看看這邊文章:
http://tech.lede.com/2017/06/15/rd/server/hystrix/