1. 程式人生 > >記一次Grpc接口壓力測試&性能調優

記一次Grpc接口壓力測試&性能調優

說明 xmx 時也 希望 服務器安裝 調整 壓力測試 分鐘 f11

〇、經驗總結:

  • 如果在壓測過程中,壓力始終上不去,可以考慮是施壓機器並發上不去,或者被壓機器請求處理不過來。
  • 施壓上不去或者被壓機器請求處理不過來,是因為機器CPU瓶頸?內存瓶頸?端口數量瓶頸?逐步排查定位。
  • 類似於Grpc這類需要建立rpc連接的請求,可擴展端口的數量會影響並發時連接建立數量。
  • 長鏈路的壓測鏈,在定位問題時可以先從短鏈開始逐步排查擴展到長鏈,最終完成整個鏈路的壓力測試。
  • 關註壓測過程中可能出現的異常現象,哪怕是很不明顯的地方,都可能會存在一個BUG。

一、背景說明
這周我們對項目裏新增的幾個接口進行了壓力測試,期間遇到了一些之前沒有遇到過的坑,走了一些彎路,在這裏對這次壓力測試經歷進行總結復盤,同時也希望能給看到這篇文章的諸位提供一些淺顯的思路。

首先介紹一下我們項目的結構。服務入口是一個網關模塊,提供一個Grpc類型的接口,數據傳輸模式是一元數據模式。網關模塊與其他業務模塊之間通過Dubbo接口進行交互。
服務的架構概況圖如下:
技術分享圖片

該業務接口部署的服務器配置和部署MySQL組件的服務器配置一致,都是4核8G,50G普通硬盤,並且處於同一個內網網段,我們預估的性能指標要達到300並發,600TPS。
在壓力測試過程中,我們重點關註TPS、GC次數、CPU占用率和接口響應時間等指標。

二、測試過程
完成項目部署後,我們開始編輯jemeter測試腳本,設置壓力測試的標準為300個並發線程,在10秒內全部啟動,持續壓測時間15分鐘,接著開始啟動jemeter腳本進行測試。

1、第一次壓力測試
垃圾收集策略包括:老年代啟用CMS垃圾收集算法,新生代啟用ParNew垃圾收集算法,新生代最大存活周期為15次minorGC,FullGC時使用CMS算法,並開啟CMS中的並行標記。
根據前幾次的壓力測試經驗,我們將初始堆內存設置為2048MB,因為偏小的堆內存設置容易在壓力測試時被撐爆。
JVM內存分配:最大/最小堆內存為2048MB,Eden和Survivor比例為8:2,新生代和老年代的比例為1:2。由於服務器安裝的是JDK8版本,廢棄了永久代的配置。
JVM配置參數如下:

-XX:+PrintGCDetails 
-XX:+PrintGCDateStamps 
-Xloggc:/var/log/$MODULE/gc.log 
-XX:+UseConcMarkSweepGC 
-XX:+UseParNewGC 
-XX:MaxTenuringThreshold=15 
-XX:+ExplicitGCInvokesConcurrent 
-XX:+CMSParallelRemarkEnabled 
-XX:+HeapDumpOnOutOfMemoryError 
-XX:HeapDumpPath=/var/log/$MODULE 
-Xmx2048m -Xms2048m 
-XX:SurvivorRatio=8

(2)性能指標監控
top命令觀察java線程的CPU占用率(us表示用戶進程,sy表示系統進程),並使用jstac -gcutil Pid 1000 命令,定期查看虛擬機的GC情況。
一切準備就緒後,我們開始跑壓測腳本,並查看性能監控指標。
但是我們沒有看到預期來臨的壓力,而是並發到達一定值時,好像突然並發壓力中斷了,然後間隔1-2秒後壓力又重新出現,這期間接口服務器各項指標均沒有異常。很顯然,並發存在問題!
技術分享圖片

(3)問題排查與解決
上述壓測過程中出現的現象,可以細分為兩類:

  • 並發數量沒有達到預期值,而且徘徊在一個較低水平,壓測2分鐘,接口側只收到了3W多個請求,實際在施壓服務器嘗試發起了7W次請求。
  • 並發過程中出現類似壓力中斷的現象,而且並非偶然現象,壓力停頓之後壓測腳本中的斷言開始拋出錯誤信息:連接異常。
    異常信息如下:
    技術分享圖片

由於Grpc接口需要在客戶端與服務端建立RPC連接,那麽兩端都需要同時指定各自的一個端口進行數據通信。基於這一點,我們判斷出現第一種現象的原因可能有兩個:

  • 施壓機器端口受限,沒有啟動足夠的線程發起請求;
  • 被壓機器端口受限,不能接收施壓機器發過來的所有請求連接,導致連接請求丟失。
    而出現第二種現象的原因,可能正是由於端口數量受限,並發線程不能正常發起,需要等待連接端口釋放後才能繼續發起新的連接線程。
    於是我們去查看兩端機器的擴展端口列表,命令:
    cat  /proc/sys/net/ipv4/ip_local_port_range

    結果是:
    技術分享圖片

即僅放開了 32768——60999 之間的端口,數量大致也是3W左右。
然後我們重新設置了被壓機器的端口擴展列表,命令:

echo  "10000    65535" >  /proc/sys/net/ipv4/ip_local_port_range

結果是:
技術分享圖片

2、第二次壓力測試
經過第一次壓力測試調整後,我們開始對調整效果進行測試驗證。
(1)JVM配置
JVM配置沒有改變
(2)性能指標監控
查看被壓機器的GC情況和CPU使用情況,依然沒有太大變化,但請求數量稍微有所提升,說明端口擴展有一定的效果,但是不明顯。
繼續查看施壓機器的信息,接口調用的結果斷言依然存在錯誤,雖然錯誤率有所降低(跟被壓機器接收請求的數量上升有關系),錯誤信息還是連接異常。

技術分享圖片

從這個結果來看,解決問題的方向是對的,於是繼續把施壓機器的端口擴展放開,開始第三輪測試驗證。
3、第三次壓力測試
(1)JVM配置
JVM配置沒有改變
(2)性能指標監控
施壓機器和被壓機器的端口列表都放開後,grpc連接請求都正常了。在100並發和300並發兩種情況下,持續2分鐘發起的請求數相差不大,說明已經接近了兩端服務器的處理極限了。
技術分享圖片

(3)新問題暴露
本來以為這樣就OK了,然而此時突然發現被壓機器的GC開始出現異常,即YGC次數開始不變動,但是FGC頻繁。
壓測一段時間後,FGC開始出現,頻率為平均每秒2-3次。
技術分享圖片

通常出現FGC的原因,無非就是老年代被占滿了,於是查看線程的老年代堆內存情況:

jstat -gcold  PID

技術分享圖片
技術分享圖片

我的天!老年代才64KB!這是把新生代內存塞滿後,開始往可憐巴巴的老年代塞了,於是頻繁觸發FGC。
於是查看JVM參數配置,發現少了老年代的內存配置了。
老年代的堆內存配置可以通過 -XX:NewRatio=3 來設置,表示老生代:新生代的比值。即如果2GB堆內存的話,那麽老年代是1.5GB,新生代是0.5GB。
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/var/log/$MODULE/gc.log -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:MaxTenuringThreshold=15 -XX:+ExplicitGCInvokesConcurrent -XX:+CMSParallelRemarkEnabled -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/$MODULE -Xmx2048m -Xms2048m -XX:SurvivorRatio=8 -XX:NewRatio=3
配置好之後,重啟被壓測模塊,查看進程的堆內存分配情況:
技術分享圖片

記一次Grpc接口壓力測試&性能調優