性能壓測是當服務上線前,或者之後重要需求釋出流程中,需要做的必要測試;以模拟真實流量的方式,擷取目前系統的性能名額、是否存着高并發隐患、瓶頸等資訊的手段。
性能壓測處于什麼位置?或者說什麼時候去做呢?如何做?
如何做好線上環境的性能壓測、全鍊路壓測,如何做到壓測結果準确無誤,不影響外部環境、不污染資料,需要深入思考。
一、測試分類
按照上線流程:
- 單元測試
- 內建測試
- 相容性測試(前端、用戶端)
- 性能測試
運維階段:
- 安全測試
- 容災測試(故障恢複測試)
由上可以看出,性能測試是應用服務在上線過程中的一個流程,除非是内部系統,通路量少,否則都應當做一下性能測試。
安全測試又叫滲透測試,是運維和研發需要關注的問題;尤其是對資料安全要求高的系統。當一個服務穩定運作後,模拟一些攻擊手段,防止SQL注入、網絡攻擊、腳本攻擊等。
容災測試是容災方式的驗證,容災方式包括:雙機房建設、異地多活、兩地三中心。
二、壓測目的
- 了解吞吐量
- 瓶頸值
- 系統隐患
可用:一次請求,到達伺服器,有回應;那麼針對這次請求,服務是可用的,無論傳回成功或者失敗。
不可用是指請求沒有傳回,長時間等待,沒有應答,用戶端不知道結果。
高可用級别:99%、99.9%、99.99%、99.999%
大家經常說的系統可用性達到三個9,就是指99.9%。
高可靠:是指服務可靠,資料可靠。請求傳回是成功,資料寫入正确,分布式環境下資料一緻性等。
三、壓測名額
四、實作流程
1. 壓測工具選型
Jmeter + Influxdb + Grafana + Collectd
2.壓測環境準備
- 服務環境
- 壓測機
- 資料庫
- Redis
- MQ
3.壓測資料準備
- 日活資料:資料要真實、流量真實,結果才能越接近線上真實性能名額。
- 資料隔離:防止污染真實使用者資料
- 資料恢複
- 熱點資料
- 壓測預熱:盡量模拟線上真實環境情況,如緩存命中率
4.壓測開發設計
- 擋闆
- Mock資料
- 流量識别
- 壓測場景
- 加壓政策
擋闆:是用來攔截調用第三方的請求,防止壓測對第三方造成影響;提前設計好擋闆邏輯,識别壓測流量,執行擋闆邏輯,并且為了真實,還可以合理延遲一定時間再傳回Mock資料。
流量識别:nginx做負載均衡,upstream 轉發伺服器ip 時,可以擷取http請求Header的參數,我們可以給壓測流量header加标記參數,nginx擷取參數判斷後,轉發壓測流量到壓測機器。
5.監控
監控很重要,可以說是壓測中最為重要的地方,如果沒有監控,或者監控不準确,會嚴重影響壓測本身準确性。監控資料不詳細,也會影響性能調優。
一般我們需要監控的如下:
- CPU、記憶體
- IO:網絡、磁盤
- Http請求、接口方法
- JVM
- 中間件
- 資料庫負載
- 帶寬占用
其中對接口和方法的監控需要有,而且要準确,是定位性能問題的有效手段,越細化越好。
以下是一些監控工具:
1. Grafana 監控 (CPU、記憶體、成功率、http請求的RT)
2. Sentinel 監控 (接口、方法級别)
3. Visualvm 監控JVM
通過上圖可以直覺的看出JVM堆記憶體的增長情況和GC回收頻率、回收後記憶體大小。如果回收後的記憶體在持續增高,且目前請求沒有增長,說明有記憶體洩露,具體什麼對象洩漏,需要通過jmap等指令,檢視堆記憶體heap的詳情。
如以下指令:
對比GC前後的對象數量,分析是哪些沒有回收。
五、性能優化
注意:
- 慎重優化,謹記二八原則 和 Amdahl定律。
- 優化一定要伴随着壓測,通過性能名額、資料對比來論證和調整,否則沒有意義。
優化目标
- 提升吞吐量
- 降低 RT
優化方向
- 硬體層面:CPU、記憶體擴容、換SSD固态硬碟
- 軟體層面:叢集擴容、代碼優化、存儲優化(緩存、資料庫)、減少IO
優化方式
- 代碼優化:請求合并、異步、Cache、池化
- JVM調優
- Linux調優
JVM調優
1. 調優關注點
- 減少JVM不可用時間:STW(Stop the word)
- GC 頻率
- 安全點SafePoint
安全點是需要注意的問題,很多初學者會忽略安全點線程等待時間,其實STW時間包括安全點等待時間。
2. 常用GC回收器
- ParNew + CMS (低延遲、低核、小記憶體場景)
- G1 (高吞吐量、高核、大記憶體)
G1隻有在多核CPU,大記憶體情況下,才能展現出它的優勢,如果記憶體過小,表現反而不如ParNew+CMS。
那麼到底多大算大記憶體呢?
根據R大給出的建議是:以堆記憶體8g為限,大于8g用G1,小于8g用CMS。
3. JVM參數分類
- -:标準參數,所有JVM都必須實作,且向後相容。
- -X :非标準參數,預設JVM實作該參數,不保證向後相容。
- -XX:非stable參數,不同JVM有所不同,将來可能會取消。
4. G1關鍵參數
-Xms16g -Xmx16g
-XX:+UseG1GC -XX:MaxGCPauseMillis=50 -XX:InitiatingHeapOccupancyPercent=60
-verbose:gc -Xloggc:logs/gc_%p.log -XX:+DisableExplicitGC
-XX:-UseLargePages -XX:+PrintGCApplicationStoppedTime -XX:-OmitStackTraceInFastThrow
-XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1
需要注意的是,G1最好不要加新生代大小參數 -Xmn ,如果固定了新生代大小,G1就沒法根據 -XX:MaxGCPauseMillis 設定的最大期望GC時間,動态調整堆記憶體的新生代和老年代大小,相當于-XX:MaxGCPauseMillis 失效了。除非你很熟悉G1原理,加了新生代大小,配合其他參數,也可以調優出一個不錯的結果。否則還是交給G1本身的自動調整吧。
5. GC 日志
2020-01-09T11:05:34.836+0800: [GC pause (G1 Evacuation Pause) (young) ……
[Eden: 8576.0M(8576.0M)->0.0B(8576.0M) Survivors: 16.0M->16.0M Heap: 8643.7M(14.0G)->68.2M(14.0G)]
[Times: user=0.05 sys=0.04, real=0.02 secs]
2020-01-09T11:05:34.863+0800: 2043140.818: Total time for which application threads were stopped:0.0286394 seconds, Stopping threads took: 0.0002674 seconds
2020-01-09T12:01:10.488+0800: 2046476.443: Application time: 3335.6250688 seconds
2020-01-09T12:01:10.490+0800: 2046476.444: [GC pause (G1 Evacuation Pause) (young) 2046476.444:
gc日志中 Stopping threads took 就是安全點時,線程的等待時間,是以STW時間應該是:GC時間 + 安全點等待時間。
如果 Stopping threads took 較大,說明代碼程式是有問題的,需要深入檢視;
具體什麼情況會導緻安全點時間過長,請參考:
JVM安全點介紹
HBase實戰:記一次Safepoint導緻長時間STW的踩坑之旅
Linux調優
我在對業務項目、RocketMQ、Redis、codis叢集、Elasticsearch、Hbase等進行優化過程中,經常遇到一些相同的問題,我總結了下,主要有以下:
- 慎用交換區swap
優化方式:禁止swapping 或者設定 swappiness = 1
swapping會導緻gc過程從毫秒級變成分鐘級
- 善用 /dev/shm/
/dev/shm/ 是linux的檔案記憶體系統,是共享記憶體,直接從實體記憶體中開辟,根據使用情況會動态擴容。
針對一些寫入檔案,IO性能要求高的場景,可以使用/dev/shm/,例如gc日志存放。
高并發場景下,如果打開安全點日志,安全點日志是在安全點内列印,是同步的,是以日志寫入檔案速度,會嚴重影響gc快慢;應當将gc日志配置改為:
-Xloggc:/dev/shm/gc_%p.log
其他linux調優,一般大點的公司,都會有自己的運維團隊,運維就會做基本的優化,例如tcp連接配接優化等,這裡隻介紹運維可能忽略的,不知道的點,這時候就需要研發同學介入,找運維做好應用相關的Linux核心優化。