(點選上方公眾號,可快速關註)
來源:大名Dean鼎,
www.deanwangpro.com/2018/04/04/spring-cloud-zuul-threads/
ZuulException REJECTED_SEMAPHORE_EXECUTION 是一個最近在效能測試中經常遇到的異常。查詢資料發現是因為zuul預設每個路由直接用訊號量做隔離,並且預設值是100,也就是當一個路由請求的訊號量高於100那麼就拒絕服務了,傳回500。
訊號量隔離
既然預設值太小,那麼就在gateway的配置提高各個路由的訊號量再實驗。
routes:
linkflow:
path: /api1/**
serviceId: lf
stripPrefix: false
semaphore:
maxSemaphores: 2000
oauth:
path: /api2/**
serviceId: lf
stripPrefix: false
semaphore:
maxSemaphores: 1000
兩個路由的訊號量分開提高到2000和1000。我們再用gatling測試一下。
setUp(scn.inject(rampUsers(200) over (3 seconds)).protocols(httpConf))
這是我們的模型,3s內啟動200個使用者,順序訪問5個API。所以會有1000個request。機器配置只有2核16G,並且是docker化的資料庫。所以整體效能不高。
看結果仍然有57個KO,但是比之前1000個Request有900個KO的比例好很多了。
執行緒隔離
Edgware版本的spring cloud提供了另一種基於執行緒池的隔離機制。實現起來也非常簡單,
zuul:
ribbon-isolation-strategy: THREAD
thread-pool:
use-separate-thread-pools: true
thread-pool-key-prefix: zuulgw
hystrix:
threadpool:
default:
coreSize: 50
maximumSize: 10000
allowMaximumSizeToDivergeFromCoreSize: true
maxQueueSize: -1
execution:
isolation:
thread:
timeoutInMilliseconds: 60000
use-separate-thread-pools的意思是每個路由都有自己的執行緒池,而不是共享一個。
thread-pool-key-prefix會指定一個執行緒池字首方便除錯。
hystrix的部分主要設定執行緒池的大小,這裡設定了10000,其實並不是越大越好。執行緒池越大削峰填谷的效果越顯著,也就是時間換空間。系統的整體負載會上升,導致響應時間越來越長,那麼當響應時間超過某個限度,其實系統也算是不可用了。後面可以看到資料。
這次沒有500的情況了,1000個Request都正常傳回了。
比較
從幾張圖對比下兩種隔離的效果,上圖是訊號量隔離,下圖是執行緒隔離。
響應時間分佈
直觀上能發現使用執行緒隔離的分佈更好看一些,600ms內的響應會更多一些。
QPS
兩張圖展示的是同一時刻的Request和Response的數量。
先看訊號量隔離的場景,Response per second是逐步提升的,但是達到一個量級後,gateway開始拒絕服務。猜測是超過了訊號量的限制或是超時?
執行緒隔離的這張就比較有意思了,可以看到Request per second上升的速度要比上面的快,說明系統是試圖接收更多的請求然後分發給執行緒池。再看在某個時間點Response per second反而開始下降,因為執行緒不斷的建立消耗了大量的系統資源,響應變慢。之後因為請求少了,負載降低,Response又開始抬升。所以執行緒池也並非越大越好,需要不斷除錯尋找一個平衡點。
小結
執行緒池提供了比訊號量更好的隔離機制,並且從實際測試發現高吞吐場景下可以完成更多的請求。但是訊號量隔離的開銷更小,對於本身就是10ms以內的系統,顯然訊號量更合適。
看完本文有收穫?請轉發分享給更多人
關註「ImportNew」,提升Java技能