SpringCloud從入門到進階(八)——單點部署Zuul的壓力測試與調優(一)


內容

  作為微服務架構系統的入口,毫無疑問,Zuul的並發性能直接決定了整個系統的並發性能。本文結合前幾篇文章的內容,在雲服務器中部署了包含Eureka Server,Zuul等組件的1.0版本的微服務架構,並進行單點部署Zuul的壓力測試,對其並發性能一探究竟。

版本

​   JVM監測工具:JVisualVM

​   壓力測試工具:Apache Bench 2.3

​   JDK:1.8.0_171

  SpringBoot:1.5.9.RELEASE

  SpringCloud:Dalston.SR1

環境

image

  處理器具體型號為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

​  執行下面的指令部署路由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
測試一:50個並發用戶,執行50000次請求
1.1.1直接調用sayHello接口

​  直接調用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左右(結論一)

1543906800483

1.1.2通過路由Zuul調用sayHello接口(不做負載均衡)

  系統吞吐量在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為系統的瓶頸。

1542880295196

  Service資源使用情況

  壓測過程中,Service服務器的CPU使用率為50%,堆內存的使用最大為270MB(堆空間為512MB),實時線程從48增加到75。可見由於Zuul請求轉發的不及時,Service端的CPU和內存都有富余,Zuul成為微服務架構的瓶頸(結論二)。通過線程數量的變化可知,Zuul端雖然有50個線程轉發用戶請求,但是在Service端,只有大概40個線程處理請求Zuul端轉發請求的線程數與Service端處理請求的線程數之間是什么關系呢?(問題一)這里先暫且保留這個問題,在后續的文章中再具體解釋。

1542880319708

1.2.1直接調用timeConsuming(200ms)接口

  系統吞吐量在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和內存都有富余。

1542880366318

1.2.2通過Zuul調用timeConsuming接口

  系統吞吐量在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有富余。

1542880411652

  Service資源使用情況

  壓測過程中,Service服務器的CPU使用率在7%左右,堆內存的使用最大為270MB(堆空間為512MB),實時線程從48增加到89。可見Service端的CPU和內存都有富余,可以承受更大的並發量。

1542880441823

測試二:200個並發用戶,執行50000次請求

  總請求次數不變,將並發用戶數增大到200,來探究並發用戶數增加與系統吞吐量的關系。

2.1.1直接調用sayHello接口

  系統吞吐量在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。

1542880934420

2.1.2通過Zuul調用sayHello接口

  系統吞吐量在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正常的情況下出現服務熔斷呢?這個記為問題二,同樣在后續文章中進行解讀。

1542897074736

​  Zuul資源使用情況:

  壓測過程中,Zuul服務器的CPU使用率為100%,堆內存的使用最大為500MB(堆空間為512MB)並且伴有頻繁的GC,實時線程從79增加到269。

1542881465597

  Service資源使用情況

  壓測過程中,Service服務器的CPU使用率為55%,堆內存的使用最大為390MB(堆空間為580MB),實時線程從49增加到80。可見Zuul請求轉發的不及時,微服務端的CPU和內存都有富余(結論二)。通過線程數量的變化可知,Zuul端即使有200個線程轉發用戶請求,但是在Service端,仍然只有大概40個線程處理請求(問題一)

1542881503282

2.2.1直接調用timeConsuming方法

  系統吞吐量在 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和內存仍然有富余,系統的吞吐量可以隨着並發線程的增加,同步增大。

1542898756192

2.2.2通過Zuul調用timeConsuming方法

  系統吞吐量在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資源已經耗盡(結論六)。

1542898001558

  Service資源使用情況

  壓測過程中,Service服務器的CPU使用率穩定在5%以內,堆內存的使用最大為280MB(堆空間為512MB),實時線程從48增加到88。可見發生服務熔斷后,Service端的CPU和內存資源都有很大的釋放(結論六)。

1542897983129

測試三:500個並發用戶,執行50000次請求
3.1.1直接調用sayHello接口

  系統吞吐量在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的線程沒有隨並發用戶數的進一步增多而增大呢?(問題三),這個問題仍在后續文章中進行解釋。

1542900078221

3.1.2通過路由Zuul調用sayHello接口(不做負載均衡)

  系統吞吐量在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了(問題三)。

1542900779533

  Service資源使用情況

  壓測過程中,Service服務器的CPU使用率在50%以內,堆內存的使用最大為330MB(堆空間為580MB),實時線程從48增加到69。Zuul請求熔斷,微服務端的CPU和內存都有富余。Service端,只有大概30個線程處理請求。

1542900820855

3.2.1直接調用timeConsuming方法

  系統吞吐量在 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和內存仍然有富余,系統的吞吐量可以隨着並發線程的增加,同步增大。

1542901393535

3.2.2通過Zuul調用timeConsuming方法

  系統吞吐量在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資源已經耗盡。

1542902075144

  Service資源使用情況

  壓測過程中,Service服務器的CPU使用率穩定在5%以內,堆內存的使用最大為340MB(堆空間為590MB),實時線程從48增加到88(問題一)。可見發生服務熔斷后,Service端的CPU和內存資源都有很大的釋放(結論六)。

1542902097523

本文總結

六個結論

  在本文的三種壓力測試過程中,我們得到了六個結論:

  結論一:單台2核服務器能承受的最大吞吐量在5600左右。

​  結論二:在Zuul成為微服務架構的瓶頸時,由於請求轉發的不及時,Service端的工作不飽和。因此要選擇好Zuul的配置,避免出現性能瓶頸。

  結論三:當Zuul的CPU高負荷運轉時,其轉發請求所帶來的延遲就越高。因此要選擇好Zuul的配置,盡可能降低Zuul轉發帶來的延遲。

  結論四:在CPU成為瓶頸時,即使增大並發線程的數量,系統吞吐量也不會增大,反而會由於堆內存的開銷變大,造成系統吞吐量的減少,並且用戶等待時間會與並發線程數等比例增大。

  結論五:在CPU和內存資源都充裕的情況下,增大並發線程的數量,系統的吞吐量會等比例增大,請求平均處理時間會隨之降低,但請求平均等待時間不會改變,也就是用戶體驗並不會改變。

  結論六:在高並發情況下如果Zuul發生服務熔斷,Zuul服務器的CPU負荷會增大,甚至會耗盡;系統的吞吐量雖然增加,但是請求出錯,會造成不好的用戶體驗。同時Service端的請求梳理會大幅度減少,其CPU和內存的負荷會大幅度降低。

三個問題

  同樣,我們也遇到了三個問題:

  問題一:Zuul端轉發請求的線程數與Service端處理請求的線程數之間是什么關系呢?

  問題二:Zuul為什么會在Serivce正常的情況下出現服務熔斷呢?

  問題三:為什么Service的並發線程數量達到200后沒有隨並發用戶數的進一步增大而增大呢?

  下文,我們將針對這三個問題進行剖析,並通過參數調優解決高並發的處理問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM