一、首先要明白Semaphore和線程池各自是干什么?
信號量Semaphore是一個並發工具類,用來控制可同時並發的線程數,其內部維護了一組虛擬許可,通過構造器指定許可的數量,每次線程執行操作時先通過acquire方法獲得許可,執行完畢再通過release方法釋放許可。如果無可用許可,那么acquire方法將一直阻塞,直到其它線程釋放許可。
線程池用來控制實際工作的線程數量,通過線程復用的方式來減小內存開銷。線程池可同時工作的線程數量是一定的,超過該數量的線程需進入線程隊列等待,直到有可用的工作線程來執行任務。
使用Seamphore,你創建了多少線程,實際就會有多少線程進行執行,只是可同時執行的線程數量會受到限制。但使用線程池,你創建的線程只是作為任務提交給線程池執行,實際工作的線程由線程池創建,並且實際工作的線程數量由線程池自己管理。
簡單來說,線程池實際工作的線程是work線程,不是你自己創建的,是由線程池創建的,並由線程池自動控制實際並發的work線程數量。而Seamphore相當於一個信號燈,作用是對線程做限流,Seamphore可以對你自己創建的的線程做限流(也可以對線程池的work線程做限流),Seamphore的限流必須通過手動acquire和release來實現。
區別就是兩點:
1、實際工作的線程是誰創建的?
使用線程池,實際工作線程由線程池創建;使用Seamphore,實際工作的線程由你自己創建。
2、限流是否自動實現?
線程池自動,Seamphore手動。
二 Hystrix中的實現
先給個總結對比:
隔離方式 | 是否支持超時 | 是否支持熔斷 | 隔離原理 | 是否是異步調用 | 資源消耗 |
線程池隔離 | 支持,可直接返回 | 支持,當線程池到達maxSize后,再請求會觸發fallback接口進行熔斷 | 每個服務單獨用線程池 | 可以是異步,也可以是同步。看調用的方法 | 大,大量線程的上下文切換,容易造成機器負載高 |
信號量隔離 | 不支持,如果阻塞,只能通過調用協議(如:socket超時才能返回) | 支持,當信號量達到maxConcurrentRequests后。再請求會觸發fallback | 通過信號量的計數器 | 同步調用,不支持異步 |
小,只是個計數器 |
信號量模式
在該模式下,接收請求和執行下游依賴在同一個線程內完成,不存在線程上下文切換所帶來的性能開銷,所以大部分場景應該選擇信號量模式,但是在下面這種情況下,信號量模式並非是一個好的選擇。
比如一個接口中依賴了3個下游:serviceA、serviceB、serviceC,且這3個服務返回的數據互相不依賴,這種情況下如果針對A、B、C的熔斷降級使用信號量模式,那么接口耗時就等於請求A、B、C服務耗時的總和,無疑這不是好的方案。
另外,為了限制對下游依賴的並發調用量,可以配置Hystrix的execution.isolation.semaphore.maxConcurrentRequests
,當並發請求數達到閾值時,請求線程可以快速失敗,執行降級
實現也很簡單,一個簡單的計數器,當請求進入熔斷器時,執行tryAcquire()
,計數器加1,結果大於閾值的話,就返回false,發生信號量拒絕事件,執行降級邏輯。當請求離開熔斷器時,執行release()
,計數器減1。
線程池模式
在該模式下,用戶請求會被提交到各自的線程池中執行,把執行每個下游服務的線程分離,從而達到資源隔離的作用。當線程池來不及處理並且請求隊列塞滿時,新進來的請求將快速失敗,可以避免依賴問題擴散。
在信號量模式提到的問題,對所依賴的多個下游服務,通過線程池的異步執行,可以有效的提高接口性能。
優勢
- 減少所依賴服務發生故障時的影響面,比如ServiceA服務發生異常,導致請求大量超時,對應的線程池被打滿,這時並不影響ServiceB、ServiceC的調用。
- 如果接口性能有變動,可以方便的動態調整線程池的參數或者是超時時間,前提是Hystrix參數實現了動態調整。
缺點
- 請求在線程池中執行,肯定會帶來任務調度、排隊和上下文切換帶來的開銷。
- 因為涉及到跨線程,那么就存在ThreadLocal數據的傳遞問題,比如在主線程初始化的ThreadLocal變量,在線程池線程中無法獲取
三 Zuul中的實現
Zuul默認是使用信號量隔離,並且信號量的大小是100,請求的並發線程超過100就會報錯
可以調大該信號量的最大值來提高性能,配置如下:
zuul: semaphore: max-semaphores: 5000
也可以改為使用線程隔離,調大hystrix線程池線程大小,該線程池默認10個線程,配置如下:
zuul: ribbonIsolationStrategy: THREAD hystrix: threadpool: default: coreSize: 100 maximumSize: 400 allowMaximumSizeToDivergeFromCoreSize: true maxQueueSize: -1
maximumSize:最大線程數量
allowMaximumSizeToDivergeFromCoreSize:是否讓maximumSize生效,false的話則只有coreSize會生效
maxQueueSize:線程池的隊列大小,-1代表使用SynchronousQueue隊列