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

〇、經驗總結:java

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

1、背景說明
這周咱們對項目裏新增的幾個接口進行了壓力測試,期間遇到了一些以前沒有遇到過的坑,走了一些彎路,在這裏對此次壓力測試經歷進行總結覆盤,同時也但願能給看到這篇文章的諸位提供一些淺顯的思路。
首先介紹一下咱們項目的結構。服務入口是一個網關模塊,提供一個Grpc類型的接口,數據傳輸模式是一元數據模式。網關模塊與其餘業務模塊之間經過Dubbo接口進行交互。
服務的架構概況圖以下:
記一次Grpc接口壓力測試&性能調優算法

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

2、測試過程
完成項目部署後,咱們開始編輯jemeter測試腳本,設置壓力測試的標準爲300個併發線程,在10秒內所有啓動,持續壓測時間15分鐘,接着開始啓動jemeter腳本進行測試。
一、第一次壓力測試
垃圾收集策略包括:老年代啓用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秒後壓力又從新出現,這期間接口服務器各項指標均沒有異常。很顯然,併發存在問題!
記一次Grpc接口壓力測試&性能調優併發

(3)問題排查與解決
上述壓測過程當中出現的現象,能夠細分爲兩類:ide

  • 併發數量沒有達到預期值,並且徘徊在一個較低水平,壓測2分鐘,接口側只收到了3W多個請求,實際在施壓服務器嘗試發起了7W次請求。
  • 併發過程當中出現相似壓力中斷的現象,並且並不是偶然現象,壓力停頓以後壓測腳本中的斷言開始拋出錯誤信息:鏈接異常。
    異常信息以下:
    記一次Grpc接口壓力測試&性能調優

因爲Grpc接口須要在客戶端與服務端創建RPC鏈接,那麼兩端都須要同時指定各自的一個端口進行數據通訊。基於這一點,咱們判斷出現第一種現象的緣由可能有兩個:性能

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

    結果是:
    記一次Grpc接口壓力測試&性能調優測試

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

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

結果是:
記一次Grpc接口壓力測試&性能調優3d

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

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

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

(3)新問題暴露
原本覺得這樣就OK了,然而此時忽然發現被壓機器的GC開始出現異常,即YGC次數開始不變更,可是FGC頻繁。
壓測一段時間後,FGC開始出現,頻率爲平均每秒2-3次。
記一次Grpc接口壓力測試&性能調優

一般出現FGC的緣由,無非就是老年代被佔滿了,因而查看線程的老年代堆內存狀況:

jstat -gcold  PID

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

個人天!老年代才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接口壓力測試&性能調優

相關文章
相關標籤/搜索