內容html
做爲微服務架構系統的入口,毫無疑問,Zuul的併發性能直接決定了整個系統的併發性能。本文結合前幾篇文章的內容,在雲服務器中部署了包含Eureka Server,Zuul等組件的1.0版本的微服務架構,並進行單點部署Zuul的壓力測試,對其併發性能一探究竟。java
版本spring
JVM監測工具:JVisualVM數據庫
壓力測試工具:Apache Bench 2.3緩存
JDK:1.8.0_171安全
SpringBoot:1.5.9.RELEASE服務器
SpringCloud:Dalston.SR1網絡
環境架構
處理器具體型號爲Intel xeon(skylake) platinum 8163,主頻2.5GHz。併發
說明
轉載請說明出處:SpringCloud從入門到進階(八)——單點部署Zuul的壓力測試與調優(一)
步驟
微服務架構中,全部的請求都須要通過Zuul的轉發才能到達具體的微服務。所以Zuul的併發量和可用性將直接影響甚至決定整個系統的併發量和可用性。在本篇文章中,咱們使用壓力測試工具Apache Bench,在局域網範圍內搭建環境對特定接口進行壓力測試,所以本示例只是考察CPU和內存對Zuul和微服務併發能力的影響,網絡帶寬、緩存、數據庫、磁盤IO等因素不在本實例的討論範圍內,測試系統的吞吐量、服務端請求平均處理時間、用戶請求平均等待時間等參數。
Apache Bench的安裝、使用請參考Linux入門實踐筆記(六)——壓力測試工具Apache Bench的安裝、使用和結果解讀。下面,咱們再重溫下HTTP服務器性能指標:
吞吐量(Requests per second)是在某個併發度下服務器每秒處理的請求數。它是服務器併發處理能力的量化描述,單位是reqs/s。計算公式爲:總請求數/處理請求的總耗時。 吞吐量越大說明服務器的性能越好。
請求平均處理時間(Time per request,across all concurrent requests)是服務器處理請求的平均時間,計算公式爲:處理請求的總耗時/總請求數。它是服務器吞吐量的倒數。也等於,用戶請求平均等待時間/併發用戶數。
請求平均等待時間(Time per request)是用戶等待請求響應的平均時間,計算公式爲:處理請求的總耗時/(總請求數/併發用戶數)。
「請求平均處理時間」和「請求平均等待時間」兩個概念很是容易混淆,舉一個例子進行說明:好比100個用戶同時執行上傳文檔的操做,那麼併發用戶數爲100,假設服務器能夠同時處理這100個請求,而且每一個文件上傳操做的耗時都是1s。那麼請求總耗時時間爲1s,吞吐量爲100reqs/s。請求平均處理時間爲0.01s,請求平均等待時間爲1s。也就是說,請求平均處理時間是從服務器的角度出發的,請求平均等待時間是從用戶的角度出發的。
執行下面的指令部署路由Zuul,將jvm的棧空間設置爲512MB,並在本地的7199端口開啓jmx監控,用來檢測jvm的運行狀況。由SpringCloud從入門到進階(五)——路由接入Zuul及其單點部署可知,Zuul的路由規則爲:"/routea/..." 匹配到微服務"application-serviceA"。
[user@ServerA7 jars]$ java -Dspring.profiles.active=peer2 -Dcom.sun.management.jmxremote.port=7199 #供JMX客戶端遠程鏈接用的端口號
-Dcom.sun.management.jmxremote.ssl=false #關閉帳號密碼認證,不安全,僅在開發階段使用
-Dcom.sun.management.jmxremote.authenticate=false #關閉SSL
-Djava.rmi.server.hostname=106.117.142.x #指定本機供遠程訪問的IP地址,此處是本機的公網IP
-Xms512m -Xmx512m -jar zuul-1.0-SNAPSHOT.jar &
若是按照上述參數配置,仍然遠程訪問JVM,能夠參考Linux入門實踐筆記(七)——雲服務器中配置Java項目的JMX鏈接失敗問題解決記錄。
此處在SpringCloud從入門到進階(六)——使用SpringBoot搭建微服務的基礎之上,在DemoController中增長測試接口"/timeconsume/{length}",使用sayHello方法和timeConsuming方法分別模擬簡單操做和耗時操做(由代碼可知,接口處理與網絡帶寬、緩存、數據庫、磁盤IO無關)。
@PostMapping("/hello/{name}") public String sayHello(@PathVariable(value = "name") String name, @RequestParam(value = "from") String user){ String content="Hello "+name+",this is DemoTest.From "+user+"@:"+instanceID+"."; logger.log(Level.INFO,content); return content; } @GetMapping("/timeconsume/{length}") public String timeConsuming(@PathVariable(value = "length") int length){ try { Thread.sleep(length); }catch (Exception e){ e.printStackTrace(); } String content="Successfully sleep "+length+" ms."; logger.log(Level.INFO,content); return content; }
參照下面指令啓動ServiceA的實例,一樣在本地的7199端口開啓jmx監控,將jvm的棧空間設置爲起始512MB,最大1024MB。
[user@ServerA2 jars]$ java -Dspring.profiles.active=peer1 --Dcom.sun.management.jmxremote.port=7199 #供JMX客戶端遠程鏈接用的端口號
-Dcom.sun.management.jmxremote.ssl=false #關閉帳號密碼認證,不安全,僅在開發階段使用
-Dcom.sun.management.jmxremote.authenticate=false #關閉SSL
-Djava.rmi.server.hostname=106.117.142.x #指定本機供遠程訪問的IP地址,此處是本機的公網IP
-Xms512m -Xmx1024m -jar service-1.0-SNAPSHOT.jar &
在測試路徑下建立ab的post測試所須要的參數文件params,內容爲:
[user@ServerA6 ab]$ cat params from=lee
直接調用sayHello接口,是爲了記錄該接口的處理速度,用於與經過Zuul調用作比較,以評估Zuul轉發對請求延遲的影響。系統吞吐量在5600(請求/秒)左右,請求平均處理時間爲0.177ms,請求平均等待時間爲8.869ms,50000次請求都執行成功。
注:多測試幾回,吞吐量會隨着虛擬機指令的優化逐步穩定在5600左右。
[user@ServerA6 ab]$ ab -n 50000 -c 50 -p params -T application/x-www-form-urlencoded
http://172.26.125.115:8881/test/hello/leo
Time taken for tests: 8.869 seconds #50000次請求都執行成功
Complete requests: 50000 Failed requests: 0
#系統吞吐量在5600(請求/秒)左右
Requests per second: 5637.54 [#/sec] (mean) #請求平均等待時間爲8.869ms
Time per request: 8.869 [ms] (mean) #請求平均處理時間爲0.177ms
Time per request: 0.177 [ms] (mean, across all concurrent requests)
Service資源使用狀況:
壓測過程當中,CPU使用率在80%,堆內存的使用最大爲220MB(堆空間爲512MB),實時線程從44增長到85。此時CPU成了系統吞吐量進一步提高的瓶頸,此時的系統吞吐量能夠視爲單臺2核服務器能承受的最大吞吐量,即5600左右(結論一)。
系統吞吐量在4000(請求/秒)左右,請求平均處理時間爲0.246ms,請求平均等待時間爲12.297ms,50000次請求都執行成功。跟1.1.1的測試比較,可知,Zuul轉發後,平均每一個請求的等待時間增長了3.428ms。
[user@ServerA6 ab]$ ab -n 50000 -c 50 -p params -T
application/x-www-form-urlencoded http://172.26.125.117:7082/v1/routea/test/hello/leo
Time taken for tests: 12.297 seconds Complete requests: 50000 Failed requests: 0 Requests per second: 4066.11 [#/sec] (mean)
Time per request: 12.297 [ms] (mean) Time per request: 0.246 [ms] (mean, across all concurrent requests)
Zuul資源使用狀況:
壓測過程當中,Zuul服務器的CPU使用率爲100%,堆內存的使用最大爲470MB(堆空間爲512MB),實時線程從80增長到120。可見CPU爲系統的瓶頸。
Service資源使用狀況
壓測過程當中,Service服務器的CPU使用率爲50%,堆內存的使用最大爲270MB(堆空間爲512MB),實時線程從48增長到75。可見因爲Zuul請求轉發的不及時,Service端的CPU和內存都有富餘,Zuul成爲微服務架構的瓶頸(結論二)。經過線程數量的變化可知,Zuul端雖然有50個線程轉發用戶請求,可是在Service端,只有大概40個線程處理請求。Zuul端轉發請求的線程數與Service端處理請求的線程數之間是什麼關係呢?(問題一)這裏先暫且保留這個問題,在後續的文章中再具體解釋。
系統吞吐量在250(請求/秒)左右,請求平均處理時間爲4.032ms,請求平均等待時間爲201.622ms,50000次請求都執行成功。
[user@ServerA6 ab]$ ab -n 50000 -c 50 http://172.26.125.115:8881/test/timeconsume/200
Time taken for tests: 201.622 seconds Complete requests: 50000 Failed requests: 0 Requests per second: 247.99 [#/sec] (mean)
Time per request: 201.622 [ms] (mean) Time per request: 4.032 [ms] (mean, across all concurrent requests)
Service資源使用狀況
壓測過程當中,Service服務器的CPU使用率起初爲60%,隨着響應的處理,逐步穩定到10%之內,堆內存的使用最大爲270MB,實時線程從48增長到90。可見Service端的CPU和內存都有富餘。
系統吞吐量在250(請求/秒)左右,請求平均處理時間爲4.064ms,請求平均等待時間爲203.210ms,50000次請求都執行成功。跟1.2.1的測試比較,可知,Zuul轉發後,平均每一個請求的等待時間增長了1.588ms。跟1.1.2的測試比較可知,Zuul在CPU資源從緊張到富餘時,轉發後請求的等待時間延遲從3.428ms降到了1.588ms。可見當Zuul的CPU高負荷運轉時,其轉發請求所帶來的延遲就越高(結論三)。
[user@ServerA6 ab]$ ab -n 50000 -c 50 http://172.26.125.117:7082/v1/routea/test/timeconsume/200
Time taken for tests: 203.210 seconds Complete requests: 50000 Failed requests: 0 Requests per second: 246.05 [#/sec] (mean)
Time per request: 203.210 [ms] (mean) Time per request: 4.064 [ms] (mean, across all concurrent requests)
Zuul資源使用狀況
壓測過程當中,Zuul服務器的CPU使用率在10%左右,堆內存的使用最大爲470MB(堆空間爲512MB),實時線程從79增長到120。此時Zuul端的CPU有富餘。
Service資源使用狀況
壓測過程當中,Service服務器的CPU使用率在7%左右,堆內存的使用最大爲270MB(堆空間爲512MB),實時線程從48增長到89。可見Service端的CPU和內存都有富餘,能夠承受更大的併發量。
總請求次數不變,將併發用戶數增大到200,來探究併發用戶數增長與系統吞吐量的關係。
系統吞吐量在5500(請求/秒)左右,請求平均處理時間爲0.183ms,請求平均等待時間爲36.592ms,50000次請求都執行成功。跟1.1.1的測試比較,因爲受CPU瓶頸影響,在併發用戶數增大4倍以後,系統的吞吐量並無增大,反而因爲併發線程增多,堆內存的開銷變大,系統吞吐量有略微的減小,而且用戶等待時間從8.869增大到36.592ms,增大了4倍多(結論四)。
[user@ServerA6 ab]$ ab -n 50000 -c 200 -p params -T application/x-www-form-urlencoded
http://172.26.125.115:8881/test/hello/leo
Time taken for tests: 9.148 seconds Complete requests: 50000 Failed requests: 0 Requests per second: 5465.68 [#/sec] (mean)
Time per request: 36.592 [ms] (mean) Time per request: 0.183 [ms] (mean, across all concurrent requests)
Service資源使用狀況
壓測過程當中,CPU使用率達到90%,堆內存的使用最大爲410MB(堆空間爲512MB),實時線程從48增長到238。
系統吞吐量在4200(請求/秒)左右,請求平均處理時間爲0.237ms,請求平均等待時間爲47.428ms,50000次請求中有24次請求失敗。跟1.1.2的測試比較,在併發用戶數增大4倍以後,因爲受CPU瓶頸影響,系統的吞吐量並無增大,反而有略微的減小,而且用戶等待時間從12.297ms增大到47.428ms,增大了4倍(結論四)。
[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.857 seconds Complete requests: 50000 Failed requests: 24 (Connect: 0, Receive: 0, Length: 24, Exceptions: 0) Requests per second: 4216.93 [#/sec] (mean)
Time per request: 47.428 [ms] (mean) Time per request: 0.237 [ms] (mean, across all concurrent requests)
測試中存在請求失敗的狀況,查詢日誌能夠看到服務熔斷的信息。那麼,Zuul爲何會在Serivce正常的狀況下出現服務熔斷呢?這個記爲問題二,一樣在後續文章中進行解讀。
Zuul資源使用狀況:
壓測過程當中,Zuul服務器的CPU使用率爲100%,堆內存的使用最大爲500MB(堆空間爲512MB)而且伴有頻繁的GC,實時線程從79增長到269。
Service資源使用狀況
壓測過程當中,Service服務器的CPU使用率爲55%,堆內存的使用最大爲390MB(堆空間爲580MB),實時線程從49增長到80。可見Zuul請求轉發的不及時,微服務端的CPU和內存都有富餘(結論二)。經過線程數量的變化可知,Zuul端即便有200個線程轉發用戶請求,可是在Service端,仍然只有大概40個線程處理請求(問題一)。
系統吞吐量在 1000(請求/秒)左右,請求平均處理時間爲1.017ms,請求平均等待時間爲203.467ms,50000次請求都執行成功。跟1.2.1的測試比較,因爲CPU和內存資源仍存在富餘,在併發用戶數增大4倍以後,系統的吞吐量增大了4倍(247.99提高到982.96),請求平均處理時間下降爲四分之一(4.032ms縮減到1.017ms),請求平均等待時間基本沒有變化(結論五)。
[user@ServerA6 ab]$ ab -n 50000 -c 200 http://172.26.125.115:8881/test/timeconsume/200
Time taken for tests: 50.867 seconds Complete requests: 50000 Failed requests: 0 Requests per second: 982.96 [#/sec] (mean)
Time per request: 203.467 [ms] (mean) Time per request: 1.017 [ms] (mean, across all concurrent requests)
Service資源使用狀況
壓測過程當中,Service服務器的CPU使用率穩定在30%之內,堆內存的使用最大爲370MB(堆空間擴充到640MB),實時線程從49增長到239。而且此時CPU和內存仍然有富餘,系統的吞吐量能夠隨着併發線程的增長,同步增大。
系統吞吐量在2600(請求/秒)左右,請求平均處理時間爲0.386ms,請求平均等待時間爲77.109ms,50000次請求中有49196次請求出錯,發生熔斷(與2.1.2都發生服務熔斷,只不過熔斷的比例大幅度增長,問題二)。跟1.2.2的測試比較,在併發用戶數增大4倍以後,因爲發生熔斷,Zuul服務器的CPU資源耗盡,系統的吞吐量雖然增長,可是請求出錯,會形成很差的用戶體驗。可是Service端的CPU和內存的負荷會大幅度下降(結論六)。
[user@ServerA6 ab]$ ab -n 5000 -c 200 -p params -T application/x-www-form-urlencoded http://172.26.125.117:7082/v1/routea/test/timeconsume/200
Time taken for tests: 19.277 seconds Complete requests: 50000 Failed requests: 49196 (Connect: 0, Receive: 0, Length: 49196, Exceptions: 0) Requests per second: 2593.73 [#/sec] (mean)
Time per request: 77.109 [ms] (mean) Time per request: 0.386 [ms] (mean, across all concurrent requests)
Zuul資源使用狀況
壓測過程當中,Zuul服務器的CPU使用率接近100%,堆內存的使用最大爲300MB(堆空間爲512MB),實時線程從76增長到266。因爲出現頻繁的服務熔斷,Zuul的CPU資源已經耗盡(結論六)。
Service資源使用狀況
壓測過程當中,Service服務器的CPU使用率穩定在5%之內,堆內存的使用最大爲280MB(堆空間爲512MB),實時線程從48增長到88。可見發生服務熔斷後,Service端的CPU和內存資源都有很大的釋放(結論六)。
系統吞吐量在5000(請求/秒)左右,請求平均處理時間爲0.201ms,請求平均等待時間爲100.580ms,50000次請求都執行成功。跟2.1.1的測試比較,在併發用戶數增大2.5倍以後,因爲CPU出現瓶頸,而且更多的併發用戶帶來額外的開銷,系統的吞吐量開始降低,系統吞吐量從5465.68降到4971.14,而且用戶等待時間從36.592ms增大到100.580ms。
[user@ServerA6 ab]$ ab -n 50000 -c 500 -p params -T application/x-www-form-urlencoded
http://172.26.125.115:8881/test/hello/leo
Time taken for tests: 10.058 seconds Complete requests: 50000 Failed requests: 0 Write errors: 0 Requests per second: 4971.14 [#/sec] (mean)
Time per request: 100.580 [ms] (mean) Time per request: 0.201 [ms] (mean, across all concurrent requests)
Service資源使用狀況:
CPU使用率達到80%,堆內存的使用最大爲450MB(堆空間擴充到710MB),實時線程從48增長到238。對比2.1.1的測試,爲何Service的線程沒有隨併發用戶數的進一步增多而增大呢?(問題三),這個問題仍在後續文章中進行解釋。
系統吞吐量在4100(請求/秒)左右,請求平均處理時間爲0.241ms,請求平均等待時間爲120.743ms,50000次請求有92次熔斷(問題二)。
[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.074 seconds Complete requests: 50000 Failed requests: 92 (Connect: 0, Receive: 0, Length: 92, Exceptions: 0) Requests per second: 4141.04 [#/sec] (mean)
Time per request: 120.743 [ms] (mean) Time per request: 0.241 [ms] (mean, across all concurrent requests)
Zuul資源使用狀況:
壓測過程當中,Zuul服務器的CPU使用率爲100%,堆內存的使用最大爲330MB(堆空間爲512MB)而且伴有頻繁的GC,實時線程從77增長到268。這裏和3.1.1同樣,Zuul在請求的併發用戶數達到500時,其併發處理線程仍保持在200了(問題三)。
Service資源使用狀況
壓測過程當中,Service服務器的CPU使用率在50%之內,堆內存的使用最大爲330MB(堆空間爲580MB),實時線程從48增長到69。Zuul請求熔斷,微服務端的CPU和內存都有富餘。Service端,只有大概30個線程處理請求。
系統吞吐量在 1000(請求/秒)左右,請求平均處理時間爲1.011ms,請求平均等待時間爲505.538ms,50000次請求都執行成功。跟2.2.1的測試比較,在併發用戶數增大2.5倍以後,系統的吞吐量增和請求平均處理時間基本沒有變化,可是請求平均等待時間從203.467ms增大到530.300ms(結論四)。
[user@ServerA6 ab]$ ab -n 50000 -c 500 http://172.26.125.115:8881/test/timeconsume/200
Time taken for tests: 50.554 seconds Complete requests: 50000 Failed requests: 0 Requests per second: 989.05 [#/sec] (mean)
Time per request: 505.538 [ms] (mean) Time per request: 1.011 [ms] (mean, across all concurrent requests)
Service資源使用狀況
壓測過程當中,Service服務器的CPU使用率穩定在30%之內,堆內存的使用最大爲370MB(堆空間擴充到640MB),實時線程從49增長到239(問題三)。而且此時CPU和內存仍然有富餘,系統的吞吐量能夠隨着併發線程的增長,同步增大。
系統吞吐量在2600(請求/秒)左右,請求平均處理時間爲0.383ms,請求平均等待時間爲191.269ms,50000次請求中有49196次請求出錯,發生熔斷(問題二)。與2.2.2的測試比較,在併發用戶數增大2.5倍以後,系統的吞吐量增和請求平均處理時間基本沒有變化,可是請求平均等待時間從77.109ms增大到191.269ms(結論四)。
[user@ServerA6 ab]$ ab -n 50000 -c 500 http://172.26.125.117:7082/v1/routea/test/timeconsume/200
Time taken for tests: 19.127 seconds Complete requests: 50000 Failed requests: 49196 (Connect: 0, Receive: 0, Length: 49196, Exceptions: 0) Requests per second: 2614.12 [#/sec] (mean)
Time per request: 191.269 [ms] (mean) Time per request: 0.383 [ms] (mean, across all concurrent requests)
Zuul資源使用狀況
壓測過程當中,Zuul服務器的CPU使用率接近100%,堆內存的使用最大爲370MB(堆空間爲512MB),實時線程從76增長到266(問題三)。因爲出現頻繁的服務熔斷,Zuul的CPU資源已經耗盡。
Service資源使用狀況
壓測過程當中,Service服務器的CPU使用率穩定在5%之內,堆內存的使用最大爲340MB(堆空間爲590MB),實時線程從48增長到88(問題一)。可見發生服務熔斷後,Service端的CPU和內存資源都有很大的釋放(結論六)。
在本文的三種壓力測試過程當中,咱們獲得了六個結論:
結論一:單臺2核服務器能承受的最大吞吐量在5600左右。
結論二:在Zuul成爲微服務架構的瓶頸時,因爲請求轉發的不及時,Service端的工做不飽和。所以要選擇好Zuul的配置,避免出現性能瓶頸。
結論三:當Zuul的CPU高負荷運轉時,其轉發請求所帶來的延遲就越高。所以要選擇好Zuul的配置,儘量下降Zuul轉發帶來的延遲。
結論四:在CPU成爲瓶頸時,即便增大併發線程的數量,系統吞吐量也不會增大,反而會因爲堆內存的開銷變大,形成系統吞吐量的減小,而且用戶等待時間會與併發線程數等比例增大。
結論五:在CPU和內存資源都充裕的狀況下,增大併發線程的數量,系統的吞吐量會等比例增大,請求平均處理時間會隨之下降,但請求平均等待時間不會改變,也就是用戶體驗並不會改變。
結論六:在高併發狀況下若是Zuul發生服務熔斷,Zuul服務器的CPU負荷會增大,甚至會耗盡;系統的吞吐量雖然增長,可是請求出錯,會形成很差的用戶體驗。同時Service端的請求梳理會大幅度減小,其CPU和內存的負荷會大幅度下降。
一樣,咱們也遇到了三個問題:
問題一:Zuul端轉發請求的線程數與Service端處理請求的線程數之間是什麼關係呢?
問題二:Zuul爲何會在Serivce正常的狀況下出現服務熔斷呢?
問題三:爲何Service的併發線程數量達到200後沒有隨併發用戶數的進一步增大而增大呢?
下文,咱們將針對這三個問題進行剖析,並經過參數調優解決高併發的處理問題。