Jmeter性能測試實踐之java請求


 前言

 

Apache Jmeter是開源、易用的性能測試工具,之前工作中用過幾次對http請求進行性能測試,對jmeter的基本操作有一些了解。最近接到開發的對java請求進行性能測試的需求,所以需要寫java請求的腳本。

Java請求的性能測試與http請求的性能測試類似,都是給遠程應用提供的服務發送請求並施壓,得到響應結果及性能數據。不同的是,http調用的是應用提供的http協議的服務,而java請求調用的是應用提供的接口服務,且需要通過編寫代碼來實現java請求的調用。

本次壓測的java請求是以RMI方式調用的,Java RMI 指的是遠程方法調用 (Remote Method Invocation)。使用這種機制能夠讓一台java虛擬機上的對象調用另一台java虛擬機上的對象的方法來獲取遠程數據,可以實現不同java虛擬機上對象之間的通信。引用網上對RMI的一句通俗的介紹:遠程調用就像將一個class放在A機器上,然后在B機器中產生一個代理對象來調用這個class的方法。下面詳細介紹一下用jmeter對java請求進行性能測試的過程。

 

 

 

1 Java測試代碼編寫

1)創建測試項目

新建一個java工程,這里我使用的IDE是eclipse。

2)引入jmeter的jar包

將{Jmeter_home}\lib\ext目錄下的ApacheJMeter_core.jar和ApacheJMeter_java.jar兩個jar包復制到測試項目的lib目錄下,這兩個包是編寫java請求性能測試代碼必須的。本例中還用到了{Jmeter_home}\lib下的avalon-framework-4.1.4.jar、commons-logging-1.2.jar、jorphan.jar和logkit-2.0.jar,將這4個jar也復制到lib目錄,並將引用的jar包都添加到項目的Build Path中。本例中開發提供了docmodel-client-0.3.jar,其中包含了建立遠程連接及調用待測方法的過程,所以需要將其及依賴的包都引入到工程中。

3)編寫測試代碼

新建測試類,該類必須繼承AbstractJavaSamplerClient類或實現JavaSamplerClient接口,需要重寫以下方法:

1、public Arguments getDefaultParameters();設置入參,已設置的參數會顯示在jmeter GUI的參數列表中

2、public void setupTest(JavaSamplerContext context);初始化方法,用於初始化性能測試的每個線程,每個線程前都會執行一次。

3、public SampleResult runTest(JavaSamplerContext arg0);性能測試的線程運行體,測試執行主體,從arg0中獲取參數,並調用被測方法,完成與服務器的交互。該方法是java Sampler實現的重點,執行次數取決於線程數和循環次數。

4、public void teardownTest(JavaSamplerContext arg0);測試結束時調用,每個線程執行一次。setupTest和teardownTest方法不需要時可以不寫。

  以上4個方法中只有runTest是必須實現的,其他3個可根據需求去覆蓋。這4個方法執行的先后順序與其前面的序號相對應,分別為:getDefaultParameters()、setupTest(JavaSamplerContext context)、runTest(JavaSamplerContext arg0)、teardownTest(JavaSamplerContext arg0)。

如果需要對多個方法進行性能測試,則需要建多個測試類,多個測試類可以放在同一個包下面,也可以放在單獨的包中。本次待測方法的功能是傳入一個docid(文章號),然后獲取該文章對應的brand(品牌)屬性、subject(話題)屬性或其他多個屬性。以其中一個獲取文章的brand屬性的測試類JmeterGetBrandAttr為例,因為不需要准備數據和恢復后期環境,所以省略了setupTest和teardownTest兩個方法。

畫紅框部分是測試代碼主體,sampleStart和sampleEnd方法調用時會分別生成一個時間戳,兩個時間戳之差就是一次java請求的響應時間,單位是ms。

需要注意的是,http請求在任何情況下都會有給客戶端一個反饋,但是java請求不一定。在設置的壓力較大時,服務器可能會吃不消直接異常退出,客戶端獲取不到任何返回值,保存返回結果的對象(如本例中的resultData)的值就為null。所以需要做空指針的判斷,保證代碼的正常運行。

其中TestGetArticleAttr.getBrandAttr(docid)是調用待測方法,傳入docid,返回文章的brand屬性。本例中需要壓測3個方法,對遠程方法的連接及對返回結果的簡單處理都寫在單獨的TestGetArticleAtrr類。

開發提供的包中提供了連接遠程對象的類ArticleModelClient,其中封裝了在RMI服務中查找相關對象的方法、對異常的處理等等,簡化了客戶端調用方法的過程。有一點需要注意的是,建立遠程對象連接的過程會花費一定時間,不能每次調用遠程方法都去建立連接,所以把建立連接的操作放到static代碼塊中,在類初始化的時候建立一次連接,之后直接調用方法即可,從而保證性能測試數據的准確性。

代碼編寫完成后,可以寫一個main方法對整個過程進行測試。

4)打成jar包

代碼編寫好並測試完成后,將項目打成jar包,打包完成后將其放到jmeter的擴展包目錄下,即{Jmeter_home}\lib\ext,並將項目的依賴包都放到{Jmeter_home}\lib目錄下。簡單的打包方法如下:

1、在eclipse選中測試代碼所在的包,右鍵選擇Export,選擇java文件夾下的JAR file,單擊next;

2、在JAR file后面的文本框中選擇jar包的導出位置和名字。因為我們的jar包用於jmeter性能測試,不需要指定Main方法的入口,所以直接單擊finish,完成jar包的導出。

3、修改MANIFEST.MF。MANIFEST.MF文件描述了jar包的相關信息,包括jar包的版本、創建人和類搜索路徑等。如果是可執行jar包,會包含Main-Class屬性,表明Main方法入口。Class-Path指定依賴的jar包,當前路徑是jar包所在目錄,若要引用當前目錄下一個子目錄中的jar包,使用以下格式:子目錄/jar包名稱,多個jar包之間用空格分隔,在任何平台上路徑分割符都是'/'。

本例中導出的jar包依賴了別的jar包,需要在MANIFEST.MF文件中指明依賴的jar包的名字。在導出的jar包上右鍵,選擇用WinRAR打開,進入META_INF目錄,打開MANIFEST.MF文件,添加依賴的jar包的名字,名字之前用空格分隔,本例中需添加如下內容:

Class-Path: lib/ApacheJMeter_core.jar lib/ApacheJMeter_java.jar lib/avalon-framework-4.1.4.jar lib/commons-logging-1.2.jar lib/jorphan.jar lib/logkit-2.0.jar lib/log4j-1.2.17.jar lib/json-lib-2.4-jdk15.jar lib/docmodel-client-0.3.jar

2 Jmeter GUI編寫測試計划

雙擊{Jmeter_home}\bin目錄下的jmeter.bat,打開jmeter界面,在測試計划中添加線程組,在線程組右鍵選擇添加,分別添加Sampler--Java請求、監聽器—查看結果數、監聽器—聚合報告及配置元件--CSV Data Set Config。

{Jmeter_home}\lib\ext目錄下繼承AbstractJavaSamplerClient類或實現JavaSamplerClient接口的測試類的類名都會出現在類名稱后面的下拉框中,最后的JaveTest和SleepTest為jmeter默認實現的2個java請求Sampler。前面3個分別對應我們之前創建的三個測試類,選擇com.ntes.getArticleAttr.JmeterGetBrandAttr類。

本次壓測的方法需要傳遞一個docid參數,且要求每次傳的值不同,所以需要將docid參數化,我們使用CSV Data Set Config設置從文件中動態獲取參數值,設置參數名稱為docid,對應的值為${docid}。

常用的設置項的解釋如下:

Filename:jmx文件同名目錄下的數據文件,常用的是csv和txt格式

File Encoding:一般為空,默認為ANSI

Variable Names:定義數據文件中各列對應的參數項

Delimiter:文件中各字段間使用的分隔符

Recycle on EOF: True時讀到文件結尾時從頭循環讀取,False時讀到文件結尾時停止讀取文件

Stop thread on EOF:True時讀到文件結尾時進程停止,False時讀到文件結尾時進程繼續。當Recycle on EOF為False且Stop Thread on EOF為true時,讀完數據文件時,進程停止。在某些要求文件中的參數值只使用一次的情況下會用到。

3 Response Data的保存

Jmeter的測試結果是以CSV(Comma-Separated Values)格式保存在jtl文件中的,每條結果中的各字段間以逗號分隔,看起來比較方便。Jtl文件中默認保存的各列數據分別為:

timeStamp,elapsed,label,responseCode,responseMessage,threadName,dataType,success,failureMessage,bytes,Latency。

Jmeter默認是不保存Response Data的。在之前對http請求做性能測試的時候,有時候需要將Response Data保存下來做分析,嘗試過在監聽器的configure中勾選Save Response Data這一項,但是發現在服務器上執行壓測時指定的jtl文件中並沒有保存這一項數據,網上可參考的關於這方面的資料也比較少,之后還嘗試去修改jmeter.properties中相關的配置項,也沒有得到預期的結果,倒騰了一陣沒效果就暫時把這個問題擱置了。

這次做java請求的壓測,又遇到這個問題。因為java請求與http請求不同,返回結果放在結果的哪一項中是自己設置的。因為之前沒法將Response Data保存下來,所以之前就先調用SampleResult.setResponseMessage()方法將返回值放到Response Message中,雖然是順利保存了響應結果,但總感覺怪怪的。因為在http請求中,Response Message用來表示本次請求的響應是否正常的,請求正常的情況下是”OK”,異常情況下是異常信息提示。在壓測結果反饋給開發之后,就想花時間再研究一下Response Data的保存。

如果要保存Response Data,需要選擇某個監聽器,點擊界面上的Configure按鈕,要同時勾選上面畫紅框的兩項,並且需要指定結果數據要寫入的文件名。不管指定的保存結果的文件格式是不是xml,結果數據都是以xml的格式存儲的。要注意的是,在服務器上執行jmeter的時候,也需要另外指定上面的保存結果信息的文件名及路徑,因為Response Data是不會保存到-l參數后面指定的jtl文件中的。對其他信息的保存也可以參考這個方法。指定的保存結果數據的文件的內容格式如下圖所示,是一個xml文件。

本例中要保存Response Data是為了對結果做一個分析,統計文章的某些屬性能計算出來的概率,並且大部分的返回結果都是”[]”,數據量很小。一般不需要做統計分析的時候不用保存Response Data,並且如果要保存的Response Data數據量很大的話,可能會影響性能數據的准確性質。

4 測試計划jmx文件簡介

Windows下使用jmeter的GUI操作,會生成jmx格式的測試計划。因為之前不太了解jmx文件,所以花時間研究了一下,截取jmx文件的前一部分內容,如下圖所示。

從第一行可以看出,這就是一個xml文件,我們使用jmeter GUI進行操作,其實就是在配置這個xml文件。

一個請求的整體是由標簽<hashTree>標記的,我們本次壓測的是java請求,JavaSampler標簽就是標記這是一個java請求,即java取樣器。如果壓測的是http請求,<hashtree>下相應的標簽為HTTPSamplerProxy。Argument.name和Argument.value分別指定了參數的名稱和取值,classname指定了java請求的類名稱。

在服務器上進行壓測時,需要設置不同的線程數,其他的配置都不需要改動,如果每次都拷貝到windows的jmeter GUI中修改再拷貝到服務器上的話會很麻煩。Jmx文件中,ThreadGroup標簽下是線程組的相關設置,其中ThreadGroup.num_threads和LoopController.loops分別對應jmeter GUI中線程組頁面中的線程數和循環次數,通過vi命令直接在服務器上修改這兩個參數即可修改線程組設置。

在服務器上跑jmeter壓測腳本

先在jmeter GUI模式下創建測試計划,保存為jmx文件。把jmeter整個文件夾拷貝到服務器上,並進入bin目錄下運行:

./jmeter.sh -n -t ***.jmx -l ***.jtl

-n指定jmeter在非GUI模式下運行,-t指定包含測試計划的jmx文件名,-l指定jtl日志文件名。執行完成后會在指定目錄下生成jtl的結果文件。

需要注意的是,剛拷貝到linux上的jmeter.sh,用戶是沒有執行權限的,使用ls –l查看文件詳情,如下:

需要為該文件添加可執行權限,在jmeter.sh所在目錄下執行命令:chmod +x jmeter.sh,在沒有指定用戶情況下,默認是為所有用戶添加執行權限,再用ls –l查看文件權限,如下:

還有一點需要注意的是jdk版本,如果測試代碼所用的jdk版本比服務器上jdk版本高的話會報錯。

 

 

6 性能測試結果分析

因為接觸性能測試的時間很短,對系統性能的理解和分析還處於初級的階段,所以此處主要講一下初學者需要注意的一些點,不涉及具體性能瓶頸的分析。

在進行性能測試之前,需要跟開發問清楚性能測試的目的,是為了找到系統能支撐的最大負載還是為了檢查在一定負載下系統的運行情況。如果是后者的話,還需要問一下服務器端設置的最大並發線程數,這樣測試時才能設置出合適的負載量。我之前做過的幾次http請求的壓測,都是為了找系統能支持的最大負載。本次壓測,我先在本地測試,給了50個線程並循環100次,結果在測試運行一段時間后就會出現需要等待很長時間的情況,一開始以為是本地網絡狀況不佳,將腳本放在服務器上跑依然會出現這個情況,后來跟開發溝通知道服務端設置了最大並發量的限制,並且只需要測試一下特定線程下系統的性能情況即可。有了這次經驗之后,在之后的性能測試前就知道要盡量問清楚測試需求了。

還有一點需要注意的是緩存對於性能的影響,有無緩存對性能的影響通常是很大的,需要跟開發確認好重點關注的是有緩存還時沒有緩存的情況。本次壓測,有一個測試類是查詢文章subject(話題)屬性的,話題屬性是實時計算出來的,如果傳入的docid是第一次調用該方法,返回話題屬性耗時會比較長,之后再對同一個docid查詢話題屬性時是直接從緩存中取數據返回,耗時大大減少。對獲取話題屬性的方法進行性能測試,在40個線程、重復100次的壓力下,沒有緩存和有緩存時的TPS分別為70/s和2200/s,可見兩種情況下的差距之大。

一般性能測試的時候還需要關注其他的一些信息,如cpu和內存的使用量,本例中因為線程數較小,對服務器cpu和內存使用量影響不大,所以沒有做詳細的記錄。

壓測完成后,需要整理一份較詳細的結果反饋給開發。本次壓測的結果如下,在沒有緩存的情況下,TPS較低,並沒有達到開發的預期,需要進行性能調優。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM