原文見(jiàn):在路上的博客:服務(wù)端性能測(cè)試-工具篇
不依賴工具,但要重視工具。壓測(cè)工具是壓測(cè)理論的產(chǎn)物,學(xué)習(xí)好工具,可以幫助更加理解性能測(cè)試的基礎(chǔ)知識(shí)。
性能測(cè)試,簡(jiǎn)而言之,就是模擬大量用戶同時(shí)訪問(wèn),測(cè)試服務(wù)是否滿足性能要求。
1、選擇合適的工具
選擇工具之前,首先了解一下,我們平常做性能測(cè)試,對(duì)性能工具的需求有哪些:
- 經(jīng)常測(cè)試的對(duì)象:Http(s)協(xié)議、WebSocket (網(wǎng)絡(luò)通信協(xié)議)、TCP/IP(測(cè)試Mysql、Redis)
- 擴(kuò)展性好:支持分布式部署
- 使用成本低:文檔多、支持UI界面操作、插件全
下面,我們從工具功能和性能兩個(gè)角度,來(lái)橫向?qū)Ρ取?/p>


綜上,考慮到免費(fèi)、開源、支持多協(xié)議、分布式擴(kuò)展、腳本開發(fā)方式、平臺(tái)支持和性能表現(xiàn),選擇Jmeter作為我們主要的性能測(cè)試工具。
2、了解Jmeter
(1)安裝
- Jdk安裝
- Jmeter.zip下載
- 解壓后,運(yùn)行Jmeter.bat或Jmeter.sh即可

- 啟動(dòng)后界面

這里的線程組,可以理解為一個(gè)用戶模型。
(2)線程組
線程組可以理解為用戶模型。

線程組控制面板包括:
- 線程組名稱
- 線程數(shù)(正在測(cè)試的用戶數(shù))
- 加速(Ramp-up)時(shí)間:從0增加到線程數(shù)的時(shí)間
- 循環(huán)計(jì)數(shù):循環(huán)測(cè)試的次數(shù)
- 調(diào)度器:
- 持續(xù)時(shí)間:表示腳本持續(xù)運(yùn)行的時(shí)間,以秒為單位,比如如果你要讓用戶持續(xù)不斷登錄1個(gè)小時(shí),你可以在文本框中填寫3600。這樣就會(huì)在1小時(shí)內(nèi)循環(huán)執(zhí)行。
- 啟動(dòng)延遲:表示腳本延遲啟動(dòng)的時(shí)間,在點(diǎn)擊啟動(dòng)后,如果啟動(dòng)時(shí)間已經(jīng)到達(dá),但是還沒(méi)有到啟動(dòng)延遲的時(shí)間,那么,啟動(dòng)延遲將會(huì)覆蓋啟動(dòng)時(shí)間,等到啟動(dòng)延遲的時(shí)間到達(dá)后,再運(yùn)行系統(tǒng)。
(3)測(cè)試Http服務(wù)
下面以開發(fā)一個(gè)Http腳本為例,來(lái)熟悉性能測(cè)試中常用的功能和插件。
A. http腳本開發(fā)
如圖中所示,界面中一目了然,協(xié)議、域名、Method、Url、參數(shù)分別填到對(duì)應(yīng)位置即可。

如果請(qǐng)求需要header,可以添加Http信息頭管理器,添加header信息。

B. 參數(shù)化
總所周知,性能測(cè)試接口必須進(jìn)行參數(shù)化,如果是固定數(shù)據(jù),可能導(dǎo)致所有請(qǐng)求全部訪問(wèn)了緩存,這樣就無(wú)法評(píng)估服務(wù)真實(shí)性能。
Jmeter參數(shù)化有3種常用方法,用戶自定義變量、csv數(shù)據(jù)文件設(shè)置、BeanShell預(yù)處理變量。
用戶自定義變量
可以設(shè)置一些常量變量。

csv數(shù)據(jù)文件設(shè)置
可設(shè)置參數(shù)化數(shù)量較多常量。

BeanShell預(yù)處理變量
當(dāng)有些變量需要加解密處理時(shí),就需要BeanShell預(yù)處理。
注意:Jmeter通過(guò)vars.put("變量名", "變量值")來(lái)聲明Jmeter變量。

import org.apache.commons.net.util.Base64;
// base64加密
String source = "哈利波特";
byte[] encodedBytes = Base64.encodeBase64(source.getBytes("UTF-8"));
String encoded = new String(encodedBytes);
vars.put("b64", encoded);
C. 斷言
斷言是用于檢查Http 請(qǐng)求Response是否滿足預(yù)期的手段。
Jmeter常用的斷言方法有3種:響應(yīng)斷言、Json斷言、Beanshell斷言。
響應(yīng)斷言
如圖所示,可以直接判斷Response body中是否包含期望字符串,方式有包含、匹配、相等...

JSON斷言
當(dāng)Response Body為Json格式時(shí),可以通過(guò)Json斷言插件,精確斷言響應(yīng)內(nèi)容。

BeanShell斷言
BeanShell是jmeter的解釋型腳本語(yǔ)言,和java語(yǔ)法大同小異,并有自己的內(nèi)置對(duì)象和方法可供使用。
vars:操作jmeter的變量:vars.get(String parmStr) 獲取jmeter的變量值;vars.put(String key,String value) 把數(shù)據(jù)存到Jmeter變量中;
prev:獲取sample返回的信息,prev.getResponseDataAsString() 獲取響應(yīng)信息;prev.getResponseCode() 獲取響應(yīng)狀態(tài)碼;


import org.json.*;
String response = prev.getResponseDataAsString(); //獲取響應(yīng)數(shù)據(jù)
JSONObject responseJson = new JSONObject(response); //轉(zhuǎn)為JSON對(duì)象
//從Json中提取值
Integer code = responseJson.getInt("code");
String result = responseJson.getString("result");
log.info("====================================");
log.info("響應(yīng)code字段:" + code);
log.info("響應(yīng)result字段:" + result);
log.info("====================================");
int exceptCode = 200;
// 判斷并斷言
if(code == exceptCode) {
Failure = false;
} else {
Failure=true;
FailureMessage="響應(yīng)code字段: 期望值:" + exceptCode + ",實(shí)際值: " + code;
}
(4)測(cè)試Mysql
參考:JMeter進(jìn)行MySQL接口性能測(cè)試實(shí)戰(zhàn):數(shù)據(jù)庫(kù)服務(wù)器測(cè)試
配置mysql連接(JDBC Connection Configuration)

需要設(shè)置一些重要的字段,這些字段將決定數(shù)據(jù)庫(kù)和JMeter之間的正確連接。 這些字段包括:
-
上半部分:
- ariables Name for created pool:變量名,在JDBC Request的時(shí)候會(huì)用同樣的名字確定是連接的那個(gè)庫(kù)和進(jìn)行的配置
- MaxNumber of Connection:數(shù)據(jù)庫(kù)最大鏈接數(shù),通常該值設(shè)置為0
- Max wait:最大的等待時(shí)間 ms毫秒,超出后會(huì)拋一個(gè)錯(cuò)誤
- Time Between Eviction Runs (ms):數(shù)據(jù)庫(kù)空閑連接的回收時(shí)間間隔
- Auto Commit:自動(dòng)提交。有三個(gè)選項(xiàng),true、false、編輯,選擇true后, 每條sql語(yǔ)句就是一個(gè)事務(wù),執(zhí)行結(jié)束后會(huì)自動(dòng)提交;false、編輯則不會(huì)提交,需要自己手動(dòng)提交
- Transaction Isolation: 數(shù)據(jù)庫(kù)事務(wù)隔離的級(jí)別設(shè)置
- TRANSACTION_NONE:不支持的事務(wù)
- TRANSACTION_READ_UNCOMMITTED :事務(wù)讀取未提交內(nèi)容
- TRANSACTION_READ_COMMITTED: 事務(wù)讀取已提交讀內(nèi)容
- TRANSACTION_SERIALIZABLE: 事務(wù)序列化(一個(gè)事務(wù)讀時(shí),其他事務(wù)只能讀,不能寫)
- DEFAULT:默認(rèn)
- TRANSACTION_REPEATABLE_READ :事務(wù)重復(fù)讀(一個(gè)事務(wù)修改數(shù)據(jù)對(duì)另一個(gè)事務(wù)不會(huì)造成影響)
-
下半部分:
- 綁定到池的變量名稱 - 它唯一地標(biāo)識(shí)配置。 JDBC Sampler將進(jìn)一步使用此名稱來(lái)標(biāo)識(shí)要使用的配置。這里將其命名為test。
- 數(shù)據(jù)庫(kù)URL:jdbc:mysql://127.0.0.1:3306/AutoTest?useSSL=true
- JDBC驅(qū)動(dòng)程序類 - com.mysql.jdbc.Driver。
- 用戶名 - root。
- 密碼 - root用戶的密碼。
- 其他字段保持不變。
JDBC Request


(5)事務(wù)定義
將多個(gè)接口定義為事務(wù),以便從業(yè)務(wù)的角度,來(lái)評(píng)估性能。
比如,完整的手機(jī)驗(yàn)證碼登錄流程,一般包含:(1)獲取手機(jī)驗(yàn)證碼;(2)用手機(jī)號(hào)和驗(yàn)證碼進(jìn)行登錄;
當(dāng)定義為登錄事務(wù)時(shí),性能測(cè)試的時(shí)候,我們就可以更宏觀的關(guān)注服務(wù)端對(duì)登錄事務(wù)的性能評(píng)估,而不是只關(guān)注到接口層。

(6)邏輯控制器
當(dāng)面對(duì)復(fù)雜業(yè)務(wù)時(shí),比如涉及到邏輯判斷的,就需要添加邏輯控制器。

比如,if控制器:
利用接口1的返回信息,進(jìn)行邏輯判斷,決定是否執(zhí)行接口2。比如根據(jù)用戶狀態(tài)信息,判斷用戶是否是VIP用戶,如果不是,則不能購(gòu)買某商品。

(7)調(diào)試插件
查看結(jié)果樹
調(diào)試腳本時(shí),可以利用查看結(jié)果樹插件來(lái)查看request和response信息,保障腳本的正確。

Jmeter執(zhí)行Log
一般是編寫B(tài)eanShell或引入jar包時(shí),腳本調(diào)試需要看log,定位腳本開發(fā)的問(wèn)題。

(8)關(guān)注的性能指標(biāo)
- QPS(TPS):每秒請(qǐng)求數(shù)
- 成功率:請(qǐng)求成功率
- 95%響應(yīng)時(shí)間:
- 99%響應(yīng)時(shí)間:
- 響應(yīng)時(shí)間和QPS變化曲線:主要看性能測(cè)試期間,服務(wù)是否穩(wěn)定。比如Java后端,在性能測(cè)試期間,不允許出現(xiàn)Full GC。


3、Jmeter優(yōu)化建議
(1)調(diào)整JMeter堆棧內(nèi)存大小
在默認(rèn)情況下現(xiàn)在JMeter5版本開辟的內(nèi)存空間為1G,這也是它的最大內(nèi)存。在實(shí)際測(cè)試的過(guò)程中,默認(rèn)JMeter內(nèi)存配置情況下,開啟3W個(gè)線程來(lái)處理http的靜態(tài)訪問(wèn)求基本上就達(dá)到了極限。再往上加的可能會(huì)報(bào)OOM的錯(cuò)誤。
有兩個(gè)JAVA的參數(shù)直接影響著JMeter能夠使用的系統(tǒng)內(nèi)存為多少,一個(gè)是“Xms”(代表初始化堆棧內(nèi)存的大?。?,一個(gè)是“Xmx(代表最大內(nèi)存池可以分配的大?。?。如果你的測(cè)試機(jī)器只跑JMeter一個(gè)JAVA應(yīng)用程序,那么建議Xmx和Xms保持一致。Xmx和Xms保持一致是為了減少JVM內(nèi)存伸縮,減少維護(hù)伸縮帶來(lái)的成本。
(2)64位操作系統(tǒng)內(nèi)存配置大小
JMeter內(nèi)存分配盡量在32位的系統(tǒng)上避免分配4G以上空間,在64位的操作系統(tǒng)盡量避免分配32G以上的空間。
(3)垃圾回收機(jī)制
參考:【JVM】hotSpot VM 7種垃圾收集器:主要特點(diǎn) 應(yīng)用場(chǎng)景 設(shè)置參數(shù) 基本運(yùn)行原理
在JAVA中有大概五類的垃圾回收機(jī)制。分為Serial收集器,ParNew收集器,Parallel收集器,Cms收集器,G1收集器。在垃圾回收機(jī)制上應(yīng)該盡量減少垃圾回收器帶來(lái)的內(nèi)存和CPU的性能損耗。當(dāng)然這些并不會(huì)對(duì)開啟線程數(shù)有著決定性的影響,屬于細(xì)節(jié)性的微調(diào)。這里比較推薦使用G1垃圾回收器。G1垃圾回收器的特點(diǎn)如下:
- 支持很大的堆,高吞吐量
- 支持多CPU垃圾回收
- 在主線程暫停時(shí),使用并行回收
- 在主線程運(yùn)行時(shí),使用并發(fā)回收。
(4)使用非GUI模式
GUI在一定程度上凍結(jié)并消耗資源,這樣會(huì)更容易產(chǎn)生一些不準(zhǔn)確的性能測(cè)試結(jié)果。
對(duì)于JMeter來(lái)說(shuō)GUI存在的意義主要在于可視化輸出結(jié)果,編寫你的測(cè)試計(jì)劃和debug你的測(cè)試計(jì)劃。
jmeter命令執(zhí)行:
jmeter -n -t /usr/local/apache-jmeter-4.0/my_threads/sfwl.jmx
參數(shù)說(shuō)明:
-h 幫助 -> 打印出有用的信息并退出
-n 非 GUI 模式 -> 在非 GUI 模式下運(yùn)行 JMeter
-t 測(cè)試文件 -> 要運(yùn)行的 JMeter 測(cè)試腳本文件
-l 日志文件 -> 記錄結(jié)果的文件
-r 遠(yuǎn)程執(zhí)行 -> 啟動(dòng)遠(yuǎn)程服務(wù)
-H 代理主機(jī) -> 設(shè)置 JMeter 使用的代理主機(jī)
-P 代理端口 -> 設(shè)置 JMeter 使用的代理主機(jī)的端口號(hào)
注意:如果未設(shè)置Jmeter的環(huán)境變量則在執(zhí)行腳本的時(shí)候需要檢查當(dāng)前目錄是否是jmeter的bin目錄下
(5)升級(jí)JMeter與JAVA版本
盡可能使用最新版本的JMeter來(lái)進(jìn)行性能測(cè)試,新版本的JMeter會(huì)使用新版的JRE和JDK,這樣會(huì)一定程度上帶來(lái)性能的提升。
(6)壓測(cè)過(guò)程中禁用監(jiān)聽器
實(shí)行壓測(cè)的過(guò)程中盡可能少使用監(jiān)聽器,最好別用。啟用監(jiān)聽器會(huì)導(dǎo)致額外的內(nèi)存開銷,這會(huì)消耗測(cè)試中為數(shù)不多的內(nèi)存資源。比較明智的做法是使用非GUI模式,將測(cè)試結(jié)果全部保存到“.jtl”的文件中。測(cè)試完成之后將jtl格式的結(jié)果文件導(dǎo)入的JMeter中再進(jìn)行結(jié)果分析。
(7)謹(jǐn)慎使用斷言
添加到測(cè)試計(jì)劃的每個(gè)測(cè)試元素都將被處理。這樣會(huì)占用較多的CPU和內(nèi)存。這種資源的占用適用于所有的斷言,尤其是比較斷言。比較斷言消耗大量資源和內(nèi)存,所以在壓力測(cè)試時(shí)慎重的使用斷言和斷言的數(shù)量。
(8)利用分布式
參考:壓測(cè)必經(jīng)之路,解讀JMeter分布式
分布式部署Jmeter,利用壓力機(jī)集群進(jìn)行壓測(cè),提升Jmeter的測(cè)試能力。