內容html
做爲微服務架構系統的入口,毫無疑問,Zuul的併發性能直接決定了整個系統的併發性能。本文結合前幾篇文章的內容,在雲服務器中部署了包含Eureka Server,Zuul等組件的1.0版本的微服務架構,並進行單點部署Zuul的壓力測試,對其併發性能一探究竟。git
環境github
說明apache
轉載請說明出處:SpringCloud從入門到進階(九)——單點部署Zuul的壓力測試與調優(二)後端
在上文中,咱們在默認配置狀況下,使用ApacheBench對微服務架構1.0中的Zuul和Service分別進行了壓力測試,每次測試共50000個請求,併發用戶數分別爲50、200、500。在測試過程當中咱們遇到了以下三個問題:tomcat
問題一:Zuul端轉發請求的線程數與後端Service處理請求的線程數不一致,它們之間是什麼關係呢?服務器
問題二:Zuul爲何會在Serivce正常的狀況下出現服務熔斷呢?網絡
問題三:爲何後端Service的併發線程數量達到200後沒有隨併發用戶數的進一步增大而增大呢?架構
下面,咱們按照由易到難的順序進行剖析這些問題,探究Zuul如何進行調優。併發
爲何後端Service的併發線程數量達到200後沒有隨併發用戶數的增大而增大呢?
SpringBoot默認使用8.5版本的Tomcat做爲內嵌的Web容器。所以Zuul或Service接收到的請求時,都是由Tomcat中Connector的線程池數量決定,也就是worker線程數。
Tomcat中默認的worker線程數的最大值爲200(官方文檔中有說明),能夠在yaml中增長server.tomcat.max-threads屬性來設置worker線程數的最大值。
所以,問題三的解決方案是在Zuul端和Service端的yaml中增長以下配置:
#增大tomcat中worker的最大線程數量
server: tomcat:
max-threads: 500
Service端完整的yaml配置文件:GitHub連接
爲何Zuul會在Serivce正常的狀況下出現服務熔斷呢?
默認狀況下,當某微服務請求的失敗比例大於50%(且請求總數大於20次)時,會觸發Zuul中斷路器的開啓,後續對該微服務的請求會發生熔斷,直到微服務的訪問恢復正常。在Serivce正常時出現服務熔斷,有多是請求端或網絡的問題,但一般是因爲hystrix的信號量小於Zuul處理請求的線程數形成的。Zuul默認使用semaphores信號量機制做爲Hystrix的隔離機制,當Zuul對後端微服務的請求數超過最大信號量數時會拋出異常,經過配置zuul.semaphore.max-semaphores能夠設置Hystrix中的最大信號量數。也就是說zuul.semaphore.max-semaphores設置的值小於server.tomcat.max-threads,會致使hystrix的信號量沒法被acquire,繼而形成服務熔斷。
確保zuul.semaphore.max-semaphores屬性值大於server.tomcat.max-threads。
Zuul端轉發請求的線程數與後端Service處理請求的線程數之間是什麼關係呢?
Zuul集成了Ribbon與Hystrix,當使用Service ID配置Zuul的路由規則時,Zuul會經過Ribbon實現負載均衡,經過Hystrix實現服務熔斷。這個過程能夠理解爲這三個動做:Zuul接收請求,Zuul轉發請求,Service接收請求。其中第一個和第三個動做,由問題三可知,分別由Zuul和Service的server.tomcat.max-threads屬性配置。
第二個動做使用了Ribbon實現負載均衡,經過設置ribbon.MaxConnectionsPerHost屬性(默認值50)和ribbon.MaxTotalConnections屬性(默認值200)能夠配置Zuul對後端微服務的最大併發請求數,這兩個參數分別表示單個後端微服務實例請求的併發數最大值和全部後端微服務實例請求併發數之和的最大值。
第二個動做同時使用Hystrix實現熔斷,Zuul默認使用semaphores信號量機制做爲Hystrix的隔離機制,當Zuul對後端微服務的請求數超過最大信號量數時會拋出異常,經過配置zuul.semaphore.max-semaphores能夠設置Hystrix中的最大信號量數。
所以經過配置上述三個屬性能夠增長每一個路徑下容許轉發請求的線程數。這三個屬性的關係用下圖粗略的進行表示:
Zuul端轉發請求的線程數與Service端處理請求的線程數的關係:
限制一:單點部署的Zuul同時處理的最大線程數爲server.tomcat.max-threads;
限制二:向全部後端Service同時轉發的請求數的最大值爲server.tomcat.max-threads、ribbon.MaxTotalConnections和zuul.semaphore.max-semaphores的最小值,這也是全部後端Service可以同時處理請求的最大併發線程數;
限制三:單個後端Service能同時處理的最大請求數爲其server.tomcat.max-threads和ribbon.MaxConnectionsPerHost中的最小值。
注意:不少博客提到使用zuul.host.maxTotalConnections與zuul.host.maxPerRouteConnections這兩個參數。通過查閱和實踐,這兩個參數在使用Service ID配置Zuul的路由規則時無效,只適用於指定微服務的url配置路由的情景。
在Zuul端的yaml配置文件中增長以下配置,爲了不由於等待時間過長形成請求處理失敗,增長Ribbon和Hystrix的超時設置:
ribbon:
#Ribbon容許最大鏈接數,即全部後端微服務實例請求併發數之和的最大值。
MaxTotalConnections: 500
#單個後端微服務實例能接收的最大請求併發數
MaxConnectionsPerHost: 500
#建議設置超時時間,以避免由於等待時間過長形成請求處理失敗(一)
#Http請求中的socketTimeout
ReadTimeout: 5000
#Http請求中的connectTimeout
ConnectTimeout: 10000 #hystrix信號量semaphore的設置,默認爲100,決定了hystrix併發請求數
zuul: semaphore:
max-semaphores: 500
#建議設置超時時間,以避免由於等待時間過長形成請求處理失敗(二)
hystrix: command:
default: execution: isolation: thread: timeoutInMilliseconds: 10000
Zuul端完整的yaml配置文件:GitHub連接
系統吞吐量達到了4200左右,請求平均處理時間爲0.236ms,請求平均等待時間爲47.165ms,50000次請求所有成功
結果:請求所有成功,問題2成功解決。
[user@ServerA6 ab]$ ab -n 50000 -c 200 -p params -T
application/x-www-form-urlencoded http://172.26.125.117:7082/v1/routea/test/hello/leo
Time taken for tests: 11.791 seconds Complete requests: 50000 Failed requests: 0 Requests per second: 4240.46 [#/sec] (mean)
Time per request: 47.165 [ms] (mean) Time per request: 0.236 [ms] (mean, across all concurrent requests)
Zuul資源使用狀況:
壓測過程當中,Zuul服務器的CPU使用率爲100%,堆內存的使用最大爲500MB(堆空間爲512MB)而且伴有頻繁的GC,實時線程從79增長到269。
Service資源使用狀況
壓測過程當中,Service服務器的CPU使用率爲55%,堆內存的使用最大爲390MB(堆空間爲580MB),實時線程從49增長到80。
系統吞吐量在4000(請求/秒)左右,請求平均處理時間爲0.246ms,請求平均等待時間爲123.168ms,50000次請求所有成功。相對於上一節中的3.1.2,在併發用戶數增大2.5倍以後,系統的吞吐量有略微的減少。
結果:請求所有成功,問題2成功解決。
[user@ServerA6 ab]$ ab -n 50000 -c 500 -p params -T application/x-www-form-urlencoded
http://172.26.125.117:7082/v1/routea/test/hello/leo
Time taken for tests: 12.317 seconds Complete requests: 50000 Failed requests: 0 Requests per second: 4059.48 [#/sec] (mean)
Time per request: 123.168 [ms] (mean) Time per request: 0.246 [ms] (mean, across all concurrent requests)
Zuul資源使用狀況:
壓測過程當中,Zuul服務器的CPU使用率爲100%,堆內存的使用最大爲470MB(堆空間爲512MB)而且伴有頻繁的GC,實時線程從71增長到492。此時CPU和內存都存在瓶頸。
結果:Zuul接收請求的線程數超過了200,達到了430+,問題三解決。
Service資源使用狀況
壓測過程當中,Service服務器的CPU使用率在50%之內,堆內存的使用最大爲330MB(堆空間爲580MB),實時線程從48增長到89,將近50個線程在處理Zuul轉發的請求。
系統吞吐量在2400(請求/秒)左右,請求平均處理時間爲0.423ms,請求平均等待時間爲211.451ms,50000次請求都執行成功。跟上一節3.2.1的測試比較,在併發用戶數增大2.5倍以後,系統的吞吐量同步增大將近2.4倍,請求平均等待時間從203.467ms變爲211.451ms。因爲線程增長會增大CPU的線程切換,而且佔用更多的內存。所以系統吞吐量沒有等比例增大、平均等待時間有微小的波動,也在情理之中。
[user@ServerA6 ab]$ ab -n 50000 -c 500 http://172.26.125.115:8881/test/timeconsume/200
Time taken for tests: 21.145 seconds Complete requests: 50000 Failed requests: 0 Requests per second: 2364.61 [#/sec] (mean)
Time per request: 211.451 [ms] (mean) Time per request: 0.423 [ms] (mean, across all concurrent requests)
Service資源使用狀況
壓測過程當中,Service服務器的CPU使用率穩定在50%之內,堆內存的使用最大爲470MB(堆空間擴充到670MB),實時線程從40增長到530。此時CPU和內存仍然有富餘,所以系統的吞吐量還會隨着併發線程的增長而同步增大,感興趣的童鞋能夠嘗試將server.tomcat.max-threads屬性設置成1000進行測試。
結果:實時線程從40增長到530,有500個線程在同時處理請求。問題三解決。
系統吞吐量在2200(請求/秒)左右,請求平均處理時間爲1.762ms,請求平均等待時間爲880.781ms,50000次請求中有47082次請求出錯,發生熔斷(問題二)。跟1.2.2的測試狀況相比,在併發用戶數增大10倍以後,系統的吞吐量同步增加9倍。
[user@ServerA6 ab]$ ab -n 50000 -c 500 http://172.26.125.117:7082/v1/routea/test/timeconsume/200
Time taken for tests: 23.348 seconds Complete requests: 50000 Failed requests: 0 Requests per second: 2141.47 [#/sec] (mean)
Time per request: 233.485 [ms] (mean) Time per request: 0.467 [ms] (mean, across all concurrent requests)
Zuul資源使用狀況
壓測過程當中,Zuul服務器的CPU使用率在65%附近波動,堆內存的使用最大爲370MB(堆空間爲512MB),實時線程從70增長到560。Zuul服務器的CPU和內存資源還有富餘。
Service資源使用狀況
壓測過程當中,Service服務器的CPU使用率在35%附近波動,堆內存的使用最大爲420MB(堆空間爲650MB),實時線程從48增長到538。Service服務器的CPU和內存資源還有富餘。
結果:Service端的處理線程數爲500,與併發請求用戶數一致,問題三解決。