1,zuul 默認的隔離級別是信號量,默認最大隔離信號量是100
信號量隔離和線程池隔離的區別如下:
https://my.oschina.net/u/867417/blog/2120713
默認設置:
2,zuul裏隔離是按服務隔離的,也就是1個服務1個信號量,非接口級別的
所以得注意zuul服務本身的線程池大小,後端服務的線程池大小,以及隔離信號量或線程池的線程池大小,防止1個線程被佔用光
3,在zuul裏,重新封裝了hytrix的一些配置名稱,導致hytrix的一些原生配置會失效
具體設置hytrix參數的setter如下:
需要通過zuulProperties重新設置的屬性如下:
- 隔離級別指定:zuul.ribbonIsolationStrategy: SEMAPHORE
- 信號隔離的默認隔離大小:semaphore.maxSemaphores = 20
- 指定服務的信號隔離級別大小:zuul.eureka.serviceId.semaphore.maxSemaphores = 20
而原生的hytrix.command.default.execution.isolation.strategy和maxConcurrentRequests的配置將失效,會被這3個覆蓋
4,如果用的是信號量隔離級別,那麼hytrix的超時將會失效
當使用線程池隔離時,因爲多了一層線程池,而且是用的RXJava實現,故可以直接支持hytrix的超時調用
如果使用的是信號量隔離,那麼hytrix的超時將會失效,但是ribbon或者socket本身的超時機制依然是有效果的,而且超時後會釋放掉信號
5,但如果是信號量隔離,依然得注意hytrix設置的超時時間,因爲它涉及到信號量的釋放
先看看hytrix信號量的實現:
信號量的設置在AbstractCommand裏:
用了個ConcurrentHashMap<String, TryableSemaphore> 去保存個計數器的設置,key對應的是commandKey, TryableSemaphore(TryableSemaphoreActual)對應計數器實現,用java的AtomicInter實現,tryAcquire()時,進行原子加incrementAndGet,如果大於設置的maxConcurrentRequests,則進行阻塞
返回fallBack;
執行完後,釋放也很簡單。原子減去,decrementAndGet。這樣看來,信號量就是一個計數器。
那麼爲什麼說和超時有關呢,因爲超時時,即使訪問線程還在阻塞,也會把當前信號量釋放。(怎麼做的,因爲hytrix超時(此時訪問線程並未超時)的後續處理部分是由RxJava控制,不是依靠訪問線程的超時)
這句會造成,如果你配置了超時1s,如:
hystrix.command.default.execution.timeout.enabled=true
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000
那麼你的信號量生效將是1s內,也就是說,過了1s,不管你socket是否超時,hytrix都會釋放掉信號量
6,在zuul裏,線程池隔離情況下,是異步訪問的,而不是異步
這一點在上篇有說到,
調用的是hytrix command的excute方法,hytrix的官網原文說明如下:
execute()
— blocks, then returns the single response received from the dependency (or throws an exception in case of an error)
execute是一個阻塞方法,也就是說,如果不合理的設置線程池的大小,和超時時間,還是有可能把zuul的線程消耗完。從而失去對服務的保護作用
總結:
zuul的複雜度很大程度因爲集成了hytrix, ribbon,導致設置超時,線程,隔離都有一定的複雜度,本身文檔確沒那麼清楚。
很多地方還是需要debug分析源碼才能避免踩坑。