ZuulException REJECTED_SEMAPHORE_EXECUTION 是一個最近在性能測試中常常遇到的異常。查詢資料發現是由於zuul默認每一個路由直接用信號量作隔離,而且默認值是100,也就是當一個路由請求的信號量高於100那麼就拒絕服務了,返回500。spring
既然默認值過小,那麼就在gateway的配置提升各個路由的信號量再實驗。docker
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化的數據庫。因此總體性能不高。api
看結果仍然有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,其實並非越大越好。線程池越大削峯填谷的效果越顯著,也就是時間換空間。系統的總體負載會上升,致使響應時間愈來愈長,那麼當響應時間超過某個限度,其實系統也算是不可用了。後面能夠看到數據。spa
此次沒有500的狀況了,1000個Request都正常返回了。線程
從幾張圖對比下兩種隔離的效果,上圖是信號量隔離,下圖是線程隔離。3d
直觀上能發現使用線程隔離的分佈更好看一些,600ms內的響應會更多一些。調試
兩張圖展現的是同一時刻的Request和Response的數量。
先看信號量隔離的場景,Response per second是逐步提高的,可是達到一個量級後,gateway開始拒絕服務。猜想是超過了信號量的限制或是超時?
線程隔離的這張就比較有意思了,能夠看到Request per second上升的速度要比上面的快,說明系統是試圖接收更多的請求而後分發給線程池。再看在某個時間點Response per second反而開始降低,由於線程不斷的建立消耗了大量的系統資源,響應變慢。以後由於請求少了,負載下降,Response又開始擡升。因此線程池也並不是越大越好,須要不斷調試尋找一個平衡點。
線程池提供了比信號量更好的隔離機制,而且從實際測試發現高吞吐場景下能夠完成更多的請求。可是信號量隔離的開銷更小,對於自己就是10ms之內的系統,顯然信號量更合適。