總結
1. 線程池模式 vs 信號量模式
線程池隔離 | 信號量隔離 | |
---|---|---|
線程 | 與調用線程非相同線程 | 與調用線程相同(tomcat/jetty線程) |
開銷 | 排隊、調度、上下文開銷等 | 無線程切換,開銷低 |
異步 | 可以是異步,也可以是同步。看調用的方法 | 同步調用,不支持異步 |
並發支持 | 支持(最大線程池大小hystrix.threadpool.default.maximumSize) | 支持(最大信號量上限maxConcurrentRequests) |
是否超時 | 支持,可直接返回 | 不支持,如果阻塞,只能通過調用協議(如:socket超時才能返回) |
是否支持熔斷 | 支持,當線程池到達maxSize后,並且工作隊列也滿,再請求會觸發fallback接口進行熔斷 |
支持,當信號量達到maxConcurrentRequests后。再請求會觸發fallback |
隔離原理 | 每個服務單獨用線程池 | 通過信號量的計數器 |
資源開銷 | 大,大量線程的上下文切換,容易造成機器負載高 | 小,只是個計數器 |
使用場景 | 當請求的服務網絡開銷比較大的時候,或者是請求比較耗時的時候。為了保證可以大量的容器(tomcat)線程可用,不會由於服務原因,一直處於阻塞或等待狀態,會pick this | 當請求不耗時,返回通常很快,不會占用容器線程太長的時間;pick this 同時也減少了線程切換的開銷。 |
下圖的左邊2/3是線程池資源隔離示意圖,右邊的1/3是信號量資源隔離示意圖,我們先來看左邊的示意圖。
我們先來看左邊的示意圖。
當用戶請求服務A和服務I的時候,tomcat的線程(圖中藍色箭頭標注)會將請求的任務交給服務A和服務I的內部線程池里面的線程(圖中橘色箭頭標注)來執行,tomcat的線程就可以去干別的事情去了,當服務A和服務I自己線程池里面的線程執行完任務之后,就會將調用的結果返回給tomcat的線程,從而實現資源的隔離,當有大量並發的時候,服務內部的線程池的數量就決定了整個服務的並發度,例如服務A的線程池大小為10個,當同時有12請求時,只會允許10個任務在執行,其他的任務被放在線程池隊列中,或者是直接走降級服務,此時,如果服務A掛了,就不會造成大量的tomcat線程被服務A拖死,服務I依然能夠提供服務。整個系統不會受太大的影響。
線程池模式的優缺點
優點:
- 一個依賴可以給予一個線程池,這個依賴的異常不會影響其他的依賴。
- 使用線程池模式可以完全隔離第三方代碼,請求線程(客戶端的線程)可以快速放回。
- 當一個失敗的依賴再次變成可用時,線程池將清理,並立即恢復可用,而不是一個長時間的恢復。
- 可以完全模擬異步調用,方便異步編程。
- 使用線程池,可以有效的進行實時監控、統計和封裝。
缺點:
- 使用線程池的缺點主要是增加了計算的開銷。每一個依賴調用都會涉及到隊列,調度,上下文切換,而這些操作都有可能在不同的線程中執行。
- Netflix 更偏向於使用線程池來隔離依賴服務,因為經過計算,線程切換的消耗在可接受范圍之內。並且能支持包括超時在內的所有功能。
2. 設置隔離模式:線程池/信號量
execution.isolation.strategy: "THREAD"/"SEMAPHORE"
@HystrixCommand( commandProperties = { //利用commandProperties更改線程池的一些默認配置 //選擇“線程池”模式、"信號量"模式 @HystrixProperty(name="execution.isolation.strategy",value = "THREAD"/"SEMAPHORE"), //超時 @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000"), //信號量大小為10,那么同時只允許10個tomcat的線程(此處是tomcat的線程,而不是服務的獨立線程池里面的線程)來訪問服務,其他的請求就會被拒絕,從而達到限流保護的作用 @HystrixProperty(name="execution.isolation.semaphore.maxConcurrentRequests",value = "10"), }, ) public List<License> getLicensesByOrg(String organizationId){ //.... }
3. 設置隔離線程池、線程數量、隊列長度、fallback方法
默認情況下,Hystrix不做線程隔離,因此容易造成服務雪崩...
如果想設置隔離線程池,需要:
@HystrixCommand( //設置一個隔離的線程池 threadPoolKey = "licenseByOrgThreadPool", threadPoolProperties = { //設置該線程池的線程數 @HystrixProperty(name = "coreSize",value="30"), //設置隊列的容量,該隊列的作用是當線程池中的線程都處於工作狀態,接下來的請求會進入該隊列 @HystrixProperty(name="maxQueueSize", value="10") } //一旦隊列中也滿了,再來的請求就執行“服務降級”fallback方法 fallbackMethod = "buildFallbackLicenseList" ) public List<License> getLicensesByOrg(String organizationId){ //... }
參考文獻
服務容錯保護斷路器Hystrix之八:Hystrix資源隔離策略