作為一名開發工程師,當我們接到需求的時候,一般就是分析需要,確定思路,編碼,自測,然后就可以讓測試人員去測試了。在自測這一步,作為開發人員,很多時候就是測一下業務流程是否正確,會不會有邏輯上的錯誤,如果沒有,幾乎就完事了。然后到了測試人員去測的時候,很多時候測試人員也只是重復了一遍開發人員的步驟,更多就是把測試用例擴大了而已,特別是一些邊界的用例。但是我們的接口在每秒10次請求沒問題不代表在每秒1000次請求沒問題,當接口部署到線上,當並發量增大的時候,很多在測試階段沒出現的問題在線上就有可能出現了。
有的公司團隊比較大,可以請到好的測試人員來做各種測試,但是對於小團隊,測試人員可能也不知道每次10次請求和每秒10000次請求的區別,這時作為開發人員的我們,在保證業務邏輯正確的情況下,還要去做壓力測試。
本篇介紹一個功能強大壓力測試工具——JMeter。
JMeter是Apache下的一個頂級項目,看看官方對它的介紹:
The Apache JMeter™ application is open source software, a 100% pure Java application designed to load test functional behavior and measure performance. It was originally designed for testing Web Applications but has since expanded to other test functions.
大概意思就是說JMeter是一個純java寫的開源軟件,用來測試可變行為和性能。起初它設計的目的是測試Web應用,但是現在已經擴展到其它的測試。
JMeter可以支持測試的應用/服務/協議有很多:Web (HTTP, HTTPS )、SOAP / REST Webservices、FTP、Database via JDBC、LDAP、Message-oriented middleware (MOM) via JMS、Mail - SMTP(S), POP3(S) and IMAP(S)、Native commands or shell scripts、TCP、Java Objects。反正就是一些服務端常用的東西都能測試就是了。
本篇介紹HTTP的壓測。
一、下載
可以自行到官網找到下載鏈接或者使用下面的鏈接進行下載:
http://mirror.bit.edu.cn/apache//jmeter/binaries/apache-jmeter-5.2.1.zip
下載后直接解壓,目錄結構如下:
-
bin:可執行腳本文件
-
docs:JMeter提供的api的文檔
-
extras:額外的文件
-
lib:JMeter本身所依賴的jar包
-
licenses:JMeter依賴的jar包的license
-
printable_docs:JMeter的介紹和使用手冊
JMeter既提供了GUI界面方便用戶使用也提供了命令行的方式讓用戶操作,我們先在windows下使用JMeter。
二、准備工作
2.1、運行環境
JMeter5需要至少JDK8,如果你的機器還沒有安裝JDK,請先安裝
2.2、創建接口
這里我們使用springboot來快速構建一個接口,主要代碼如下:
@GetMapping(value = "/test")
public String performanceTest(@RequestParam(value="name", defaultValue="") String name) {
log.info("進入測試,參數name的值為:{}", name);
if (StringUtils.isEmpty(name)) {
return "name cannot be null";
} else {
return RandomUtil.generateRandomString(16);
}
}
其中generateRandomString是一個生成指定長度隨機字符的方法。
在application.properties中簡單地指定一下log文件:
logging.file=spring.log
然后打包部署到linux下,啟動命令如下:
java -jar stress-testing-0.0.1-SNAPSHOT.jar
然后用curl命令分別訪問有參數和無參數的接口,也可以在瀏覽器訪問
三、使用JMeter測試HTTP
3.1、創建測試計划
我們雙擊打開bin/jmeter.bat,等待JMeter的GUI程序啟動,成功后如下。初次啟動默認語言是英語,你可以在菜單欄的Options->Choose Language->Chinese(Simplified)切換成簡體中文
右鍵點擊TestPlan->添加->線程(用戶)->線程組,完成后選擇Thread Group,在這個線程組上點擊右鍵,添加->取樣器->HTTP請求;再選擇HTTP請求,在這個HTTP請求上點擊右鍵,添加->監聽器->察看結果樹;再次選擇HTTP請求,在這個HTTP請求上點擊右鍵,添加->監聽器->聚合報告。完成后如圖所示
下面來解釋一下這些概念:
- TestPlan:測試計划,相當於一個工程,需要測試什么,怎么測就是定義在一個測試計划中
- Thread Group:線程組,相當於模擬的請求數。一個線程相當於一個用戶請求
- 察看結果樹:監聽發送請求時各個請求的狀態
- 聚合報告:把一次測試的數據匯總
3.2、配置參數
- 測試計划
選擇左側的Test Plan,右側的名稱就是測試計划的名字,注釋就相當於代碼中的注釋,沒什么好說的。下面有個獨立運行每個線程組,在一個測試計划中可以創建多個線程組(目前我們只有一個),比如我們不同的接口的並發量是不一樣的,這時可以根據需要創建多個線程組,分開測試。好了我們先默認不作修改。
- Thread Group
選擇左側的Thread Group,線程組也有名稱和注釋,比如我們可以填寫一些並發級別之類的信息,反正就是給人看的。下面的線程屬性就是核心配置了,前面也提到,一個線程相當於一個用戶請求。比如線程數填10,Ramp-up時間填5,循環次數填1,就表示在5秒內發送10次請求,執行一次。
- HTTP請求
選擇左側的HTTP請求,這里我們關注Web服務器和HTTP請求這兩個屬性。協議就是請求的協議,默認是http,ip填服務器地址,也可以填域名,端口號是8080;因為剛才寫的test接口只支持GET請求,方法選擇GET,路徑為url的請求路徑,GET請求的參數可以直接帶在路徑上,也可以寫到下面的參數那里,通過點擊“添加”按鈕來添加請求參數。
另外值得注意的是,左側的節點很多時候是可以重復的,但是作用域不相同。比如當前察看結果樹和聚合報告都是在HTTP請求下創建的,那么這個察看結果樹和聚合報告監聽的就是這個HTTP的結果。一個線程組下可以有多個HTTP請求,比如我們有一個H5頁面要測試,打開這個頁面可能會同時請求多個接口,在這種情況下就需要創建多個HTTP請求了。當察看結果樹和聚合報告創建在Thread Group下,那么就是監聽這個線程組下所有HTTP請求的結果。
3.3、測試
點擊界面工具欄的綠色三角按鈕開始壓測,這時可以看到spring.log文件會不斷輸出信息
等待壓測結束后,我們可以選擇左側的察看結果樹,就可以看到本輪測試的請求情況
選擇其中的請求可以在右邊看到這個請求的詳細信息,包括時間,請求數據長度,請求地址等等。
選擇聚合報告可以看到本輪測試的數據報告
解釋一下這份報告:
- 樣本:請求的次數,計算公式是線程數*循環次數,如果線程組配置勾選了永遠,那么就是你停止測試時實際發送的請求數
- 平均值:響應時間的平均用時,單位是毫秒。比如這里的平均響應時間是38毫秒
- 中位數:響應時間的中位數,單位是毫秒。
- 90%百分位:90%的響應時間小於該數值,單位是毫秒。這里有90%的響應時間小於22毫秒
- 95%百分位:含義和90%類似
- 99%百分位:含義和90%類似
- 最小值:本輪測試最小響應時間,單位是毫秒。
- 最大值:本輪測試最大響應時間,單位是毫秒。
- 異常%:本輪測試出現異常的請求比例。
- 吞吐量:可以理解為QPS,即是我們測試的接口處理請求的能力。比如這里是平均每秒可以處理2.2次請求
- 接收KB/Sec:響應數據的接收速率
- 發送KB/Sec:請求數據的發送速率
到此為止,你是不是覺得剛才我們寫的接口很完美,服務也運行得毫無破綻?好了,我們選擇界面左側的Thread Group(線程組),然后把線程數改為5000,其它不變,模擬在5秒內有5000個用戶來訪問我們的接口。然后先點擊界面上方的一個齒輪加兩把掃帚的按鈕來把結果樹和聚合報告清除,然后再點擊綠色的啟動按鈕,測試結果如下
你會發現當並發增大時,會有一部分請求出現了異常。再切換到聚合報告上,你會發現原來平均響應時間只有30多毫秒直接飆到5秒多了,異常率也出現了。這時就需要我們根據一些錯誤信息去做一些調優了,可能是系統級別的,也可以是jvm級別的,又或者是代碼本身的問題。這不是本篇所要講的,這里就不說了。
四、在JMeter中使用變量
在上面例子中在我們在填寫HTTP配置的時候,IP直接寫了IP地址,但是有一個問題,假如我們的接口在不同的機器上部署了節點,我們在對不同機器上的接口進行測試,總不能每測完一個就改一個吧,假如要測試一個服務上的30個接口,不是改到手酸了?JMeter給我們提供了變量,我們可以在HTTP請求中使用變量。
我們右鍵點擊Thread Group,添加->配置元件->用戶定義的變量,也可以在測試計划或者HTTP請求下創建,這樣作用域就是整個測試計划或者HTTP請求。然后選擇用戶定義的變量,並在右邊點擊添加按鈕,添加host和port兩個變量:
在JMeter中使用變量是通過${}來引用的,比如要引用host變量,就是${host},然后我們選擇HTTP請求,把IP地址和端口改為引用變量的形式
然后我們重新點擊啟動按鈕測試,打開察看結果樹可以看到請求還是發送到變量中定義的地址。
這樣,就算我們有30個HTTP請求,我們也可以只修改用戶定義的變量中的變量值就可以了。
為了方便后面的測試我們先把線程數改為500,然后選擇左側的Test Plan,然后點擊 文件->保存測試計划為,然后選擇存放目錄,可以得到一個jmx格式的文件,后面會用到這個文件。
五、JMeter的命令行使用
在開始通過bin/jmeter.bat啟動JMeter時,會在控制台輸出如下的信息:
從這段提示我們至少可以知道兩個信息:
- 不要用GUI的模式進行測試,而應該用CLI模式(其實是使用命令行)
- 運行參數可以改變
先說第2點,因為JMeter是用純Java寫的,是運行在JVM上的,所以它的運行會受到JVM參數的控制,默認的堆大小是1G(初始值Xms和最大值Xmx都是1G),最大的Metaspace為256M(JDK8沒有永久代的概念了,用Metaspace代替)。在bin/jmeter.bat中有一行(第150行)是用來設置JVM參數的:
set HEAP=-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m
比如我們可以把這三個參數都增大1倍,然后再啟動一下,發現JVM參數已經變了
但是官方不建議我們直接去修改啟動腳本,而是建議我們在bin目錄下創建一個名為setenv.bat然后在其中設置運行參數(如果配置過tomcat的jvm參數就會很熟悉這種做法了),我們把jmeter.bat文件的HEAP參數改為原來的值,然后在bin目錄下創建一個setenv.bat文件,並且寫入以下內容:
set HEAP=-Xms2g -Xmx2g -XX:MaxMetaspaceSize=512m
重啟JMeter,再次查看其JVM參數如下
以上就是JMeter的運行參數設置,說這么多的目的其實和第1點有關。我們的服務器基本都是Linux系統,而我們的服務器應該不會安裝GUI操作界面,因為這對於用來運行服務來說不但沒用,還會吃掉很多cpu和內存資源,可能還會讓服務器不穩定。這也是JMeter建議我們不要用GUI的方式去測試的原因。既然這樣,為什么JMeter又要提供GUI界面呢,看回JMeter的啟動控制台,有這樣一行信息:
jmeter -n -t [jmx file] -l [results file] -e -o [Path to web report folder]
這里我們關注一個參數是-t,后面接jmx文件,這個文件就是上面保存的jmx文件,它保存了JMeter壓測時需要的線程組、HTTP請求等配置參數。打開可以看到其實是一個xml格式的文件,里面有各種各樣的參數,如果沒有GUI界面,讓用戶去手寫這么一個配置文件,那就很麻煩了。
說了這么多我們正式介紹一下jmeter命令的參數,jmeter的完整參數可以通過jmeter -?
查看,這里只介紹幾個常用的參數:
-n:非GUI模式,其實就是命令行的模式
-t:后跟測試文件(jmx文件)
-l:后跟log文件,把測試的過程輸出到日志文件中
-e:測試結束后生成報告
-o:測試報告存放目錄,必須是空目錄
下面使用命令行測試:
完成后會在指定的目錄生成html格式的測試報告,其中有個index.html,我們打開它,就可以看到很漂亮的圖形化的報告了,可以拿去給你們老板看了。
六、分布式測試
上面所介紹的是在一台機器上的操作,那么就會有這么一個問題:當你設置線程數為10時,在一台4核心cpu的機器上執行,這台機器模擬出10個線程當然沒問題,但是假如你把線程數提升到10000,表面上這台機器也在努力地給你模擬10000個線程,但是它的核心數也只有區區4個,在底層其實還是操作系統在不斷切換線程來模擬這10000個線程,線程切換需要時間,發送請求也需要時間,這樣做的話除了讓測試機的cpu使用率飆升到100%之外,還可以讓你得到一份不准確甚至錯誤的數據,因為你的機器其實是沒辦法在5秒來把這10000個請求發送完畢的。這個時候我們就需要用多台機器去同時給接口機發送請求,這就是分布式測試。
說一下大致的原理,我們准備多台機器,其中一台作為master機器,其余的作為slave機器,master機器用來發送指令,slave機器去執行,原理圖如下:
這些機器的要求如下:
-
關閉防火牆或者打開響應的端口
-
在同一個子網
-
JMeter可以訪問測試的接口
-
JMeter的版本一致,JDK的版本也一致,否則可能出錯
-
必須為RMI設置SSL或者關閉它
滿足了以上的條件,先執行各個slave里bin目錄下的jmeter-server,然后開發master機器上的JMeter的bin/jmeter.properties文件,找到remote_hosts=127.0.0.1那行,並且改為slave內網地址,地址之間用英文逗號分隔,然后打開master機器上的JMeter,像單機那樣測試就可以了。
我沒有那么多機器,這里就不演示了。
七、寫在最后
本篇對JMeter的介紹就到這里,事實上,JMeter的功能很強大,可以自行到官網查閱使用手冊:https://jmeter.apache.org/usermanual/get-started.html