Apache Jmeter是開源、易用的性能測試工具桃漾,之前工作中用過幾次對http請求進(jìn)行性能測試房资,對jmeter的基本操作有一些了解喳资。最近接到開發(fā)的對java請求進(jìn)行性能測試的需求,所以需要寫java請求的腳本宜鸯。
Java請求的性能測試與http請求的性能測試類似憔古,都是給遠(yuǎn)程應(yīng)用提供的服務(wù)發(fā)送請求并施壓,得到響應(yīng)結(jié)果及性能數(shù)據(jù)淋袖。不同的是鸿市,http調(diào)用的是應(yīng)用提供的http協(xié)議的服務(wù),而java請求調(diào)用的是應(yīng)用提供的接口服務(wù),且需要通過編寫代碼來實現(xiàn)java請求的調(diào)用焰情。
本次壓測的java請求是以RMI方式調(diào)用的陌凳,Java RMI?指的是遠(yuǎn)程方法調(diào)用?(Remote Method Invocation)。使用這種機(jī)制能夠讓一臺java虛擬機(jī)上的對象調(diào)用另一臺java虛擬機(jī)上的對象的方法來獲取遠(yuǎn)程數(shù)據(jù)内舟,可以實現(xiàn)不同java虛擬機(jī)上對象之間的通信合敦。引用網(wǎng)上對RMI的一句通俗的介紹:遠(yuǎn)程調(diào)用就像將一個class放在A機(jī)器上,然后在B機(jī)器中產(chǎn)生一個代理對象來調(diào)用這個class的方法验游。下面詳細(xì)介紹一下用jmeter對java請求進(jìn)行性能測試的過程充岛。
1 Java測試代碼編寫
1)創(chuàng)建測試項目
新建一個java工程,這里我使用的IDE是eclipse耕蝉。
2)引入jmeter的jar包
將{Jmeter_home}\lib\ext目錄下的ApacheJMeter_core.jar和ApacheJMeter_java.jar兩個jar包復(fù)制到測試項目的lib目錄下裸准,這兩個包是編寫java請求性能測試代碼必須的。本例中還用到了{(lán)Jmeter_home}\lib下的avalon-framework-4.1.4.jar赔硫、commons-logging-1.2.jar炒俱、jorphan.jar和logkit-2.0.jar,將這4個jar也復(fù)制到lib目錄爪膊,并將引用的jar包都添加到項目的Build Path中权悟。本例中開發(fā)提供了docmodel-client-0.3.jar,其中包含了建立遠(yuǎn)程連接及調(diào)用待測方法的過程推盛,所以需要將其及依賴的包都引入到工程中峦阁。
3)編寫測試代碼
新建測試類,該類必須繼承AbstractJavaSamplerClient類或?qū)崿F(xiàn)JavaSamplerClient接口耘成,需要重寫以下方法:
1榔昔、public Arguments getDefaultParameters();設(shè)置入?yún)ⅲ言O(shè)置的參數(shù)會顯示在jmeter GUI的參數(shù)列表中
2瘪菌、public void setupTest(JavaSamplerContext context);初始化方法撒会,用于初始化性能測試的每個線程,每個線程前都會執(zhí)行一次师妙。
3诵肛、public SampleResult runTest(JavaSamplerContext arg0);性能測試的線程運(yùn)行體,測試執(zhí)行主體默穴,從arg0中獲取參數(shù)怔檩,并調(diào)用被測方法,完成與服務(wù)器的交互蓄诽。該方法是java Sampler實現(xiàn)的重點薛训,執(zhí)行次數(shù)取決于線程數(shù)和循環(huán)次數(shù)。
4仑氛、public void teardownTest(JavaSamplerContext arg0);測試結(jié)束時調(diào)用乙埃,每個線程執(zhí)行一次闸英。setupTest和teardownTest方法不需要時可以不寫。
以上4個方法中只有runTest是必須實現(xiàn)的膊爪,其他3個可根據(jù)需求去覆蓋自阱。這4個方法執(zhí)行的先后順序與其前面的序號相對應(yīng),分別為:getDefaultParameters()米酬、setupTest(JavaSamplerContext context)沛豌、runTest(JavaSamplerContext arg0)、teardownTest(JavaSamplerContext arg0)赃额。
如果需要對多個方法進(jìn)行性能測試加派,則需要建多個測試類,多個測試類可以放在同一個包下面跳芳,也可以放在單獨(dú)的包中芍锦。本次待測方法的功能是傳入一個docid(文章號),然后獲取該文章對應(yīng)的brand(品牌)屬性飞盆、subject(話題)屬性或其他多個屬性娄琉。以其中一個獲取文章的brand屬性的測試類JmeterGetBrandAttr為例,因為不需要準(zhǔn)備數(shù)據(jù)和恢復(fù)后期環(huán)境吓歇,所以省略了setupTest和teardownTest兩個方法孽水。
畫紅框部分是測試代碼主體,sampleStart和sampleEnd方法調(diào)用時會分別生成一個時間戳城看,兩個時間戳之差就是一次java請求的響應(yīng)時間女气,單位是ms。
需要注意的是测柠,http請求在任何情況下都會有給客戶端一個反饋炼鞠,但是java請求不一定。在設(shè)置的壓力較大時轰胁,服務(wù)器可能會吃不消直接異常退出谒主,客戶端獲取不到任何返回值,保存返回結(jié)果的對象(如本例中的resultData)的值就為null软吐。所以需要做空指針的判斷瘩将,保證代碼的正常運(yùn)行。
其中TestGetArticleAttr.getBrandAttr(docid)是調(diào)用待測方法凹耙,傳入docid,返回文章的brand屬性肠仪。本例中需要壓測3個方法肖抱,對遠(yuǎn)程方法的連接及對返回結(jié)果的簡單處理都寫在單獨(dú)的TestGetArticleAtrr類。
開發(fā)提供的包中提供了連接遠(yuǎn)程對象的類ArticleModelClient异旧,其中封裝了在RMI服務(wù)中查找相關(guān)對象的方法意述、對異常的處理等等,簡化了客戶端調(diào)用方法的過程。有一點需要注意的是荤崇,建立遠(yuǎn)程對象連接的過程會花費(fèi)一定時間拌屏,不能每次調(diào)用遠(yuǎn)程方法都去建立連接,所以把建立連接的操作放到static代碼塊中术荤,在類初始化的時候建立一次連接倚喂,之后直接調(diào)用方法即可,從而保證性能測試數(shù)據(jù)的準(zhǔn)確性瓣戚。
代碼編寫完成后端圈,可以寫一個main方法對整個過程進(jìn)行測試。
4)打成jar包
代碼編寫好并測試完成后子库,將項目打成jar包舱权,打包完成后將其放到j(luò)meter的擴(kuò)展包目錄下,即{Jmeter_home}\lib\ext仑嗅,并將項目的依賴包都放到{Jmeter_home}\lib目錄下宴倍。簡單的打包方法如下:
1、在eclipse選中測試代碼所在的包仓技,右鍵選擇Export鸵贬,選擇java文件夾下的JAR file,單擊next浑彰;
2恭理、在JAR file后面的文本框中選擇jar包的導(dǎo)出位置和名字。因為我們的jar包用于jmeter性能測試郭变,不需要指定Main方法的入口颜价,所以直接單擊finish,完成jar包的導(dǎo)出诉濒。
3周伦、修改MANIFEST.MF。MANIFEST.MF文件描述了jar包的相關(guān)信息未荒,包括jar包的版本专挪、創(chuàng)建人和類搜索路徑等。如果是可執(zhí)行jar包片排,會包含Main-Class屬性寨腔,表明Main方法入口。Class-Path指定依賴的jar包率寡,當(dāng)前路徑是jar包所在目錄迫卢,若要引用當(dāng)前目錄下一個子目錄中的jar包,使用以下格式:子目錄/jar包名稱冶共,多個jar包之間用空格分隔乾蛤,在任何平臺上路徑分割符都是'/'每界。
本例中導(dǎo)出的jar包依賴了別的jar包,需要在MANIFEST.MF文件中指明依賴的jar包的名字家卖。在導(dǎo)出的jar包上右鍵眨层,選擇用WinRAR打開,進(jìn)入META_INF目錄上荡,打開MANIFEST.MF文件趴樱,添加依賴的jar包的名字,名字之前用空格分隔榛臼,本例中需添加如下內(nèi)容:
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請求、監(jiān)聽器—查看結(jié)果數(shù)金刁、監(jiān)聽器—聚合報告及配置元件--CSV Data Set Config帅涂。
{Jmeter_home}\lib\ext目錄下繼承AbstractJavaSamplerClient類或?qū)崿F(xiàn)JavaSamplerClient接口的測試類的類名都會出現(xiàn)在類名稱后面的下拉框中,最后的JaveTest和SleepTest為jmeter默認(rèn)實現(xiàn)的2個java請求Sampler尤蛮。前面3個分別對應(yīng)我們之前創(chuàng)建的三個測試類媳友,選擇com.ntes.getArticleAttr.JmeterGetBrandAttr類。
本次壓測的方法需要傳遞一個docid參數(shù)产捞,且要求每次傳的值不同醇锚,所以需要將docid參數(shù)化,我們使用CSV Data Set Config設(shè)置從文件中動態(tài)獲取參數(shù)值坯临,設(shè)置參數(shù)名稱為docid焊唬,對應(yīng)的值為${docid}。
常用的設(shè)置項的解釋如下:
Filename:jmx文件同名目錄下的數(shù)據(jù)文件看靠,常用的是csv和txt格式
File Encoding:一般為空赶促,默認(rèn)為ANSI
Variable Names:定義數(shù)據(jù)文件中各列對應(yīng)的參數(shù)項
Delimiter:文件中各字段間使用的分隔符
Recycle on EOF:?True時讀到文件結(jié)尾時從頭循環(huán)讀取,F(xiàn)alse時讀到文件結(jié)尾時停止讀取文件
Stop thread on EOF:True時讀到文件結(jié)尾時進(jìn)程停止挟炬,F(xiàn)alse時讀到文件結(jié)尾時進(jìn)程繼續(xù)鸥滨。當(dāng)Recycle on EOF為False且Stop Thread on EOF為true時,讀完數(shù)據(jù)文件時谤祖,進(jìn)程停止婿滓。在某些要求文件中的參數(shù)值只使用一次的情況下會用到。
3 Response Data的保存
Jmeter的測試結(jié)果是以CSV(Comma-Separated Values)格式保存在jtl文件中的粥喜,每條結(jié)果中的各字段間以逗號分隔空幻,看起來比較方便。Jtl文件中默認(rèn)保存的各列數(shù)據(jù)分別為:
timeStamp,elapsed,label,responseCode,responseMessage,threadName,dataType,success,failureMessage,bytes,Latency容客。
Jmeter默認(rèn)是不保存Response Data的秕铛。在之前對http請求做性能測試的時候,有時候需要將Response Data保存下來做分析缩挑,嘗試過在監(jiān)聽器的configure中勾選Save Response Data這一項但两,但是發(fā)現(xiàn)在服務(wù)器上執(zhí)行壓測時指定的jtl文件中并沒有保存這一項數(shù)據(jù),網(wǎng)上可參考的關(guān)于這方面的資料也比較少供置,之后還嘗試去修改jmeter.properties中相關(guān)的配置項谨湘,也沒有得到預(yù)期的結(jié)果,倒騰了一陣沒效果就暫時把這個問題擱置了芥丧。
這次做java請求的壓測紧阔,又遇到這個問題。因為java請求與http請求不同续担,返回結(jié)果放在結(jié)果的哪一項中是自己設(shè)置的擅耽。因為之前沒法將Response Data保存下來,所以之前就先調(diào)用SampleResult.setResponseMessage()方法將返回值放到Response Message中物遇,雖然是順利保存了響應(yīng)結(jié)果乖仇,但總感覺怪怪的。因為在http請求中询兴,Response Message用來表示本次請求的響應(yīng)是否正常的乃沙,請求正常的情況下是”O(jiān)K”,異常情況下是異常信息提示诗舰。在壓測結(jié)果反饋給開發(fā)之后警儒,就想花時間再研究一下Response Data的保存。
如果要保存Response Data眶根,需要選擇某個監(jiān)聽器蜀铲,點擊界面上的Configure按鈕,要同時勾選上面畫紅框的兩項汛闸,并且需要指定結(jié)果數(shù)據(jù)要寫入的文件名蝙茶。不管指定的保存結(jié)果的文件格式是不是xml,結(jié)果數(shù)據(jù)都是以xml的格式存儲的诸老。要注意的是隆夯,在服務(wù)器上執(zhí)行jmeter的時候,也需要另外指定上面的保存結(jié)果信息的文件名及路徑别伏,因為Response Data是不會保存到-l參數(shù)后面指定的jtl文件中的蹄衷。對其他信息的保存也可以參考這個方法。指定的保存結(jié)果數(shù)據(jù)的文件的內(nèi)容格式如下圖所示厘肮,是一個xml文件愧口。
本例中要保存Response Data是為了對結(jié)果做一個分析,統(tǒng)計文章的某些屬性能計算出來的概率类茂,并且大部分的返回結(jié)果都是”[]”耍属,數(shù)據(jù)量很小托嚣。一般不需要做統(tǒng)計分析的時候不用保存Response Data,并且如果要保存的Response Data數(shù)據(jù)量很大的話厚骗,可能會影響性能數(shù)據(jù)的準(zhǔn)確性質(zhì)示启。
4?測試計劃jmx文件簡介
Windows下使用jmeter的GUI操作,會生成jmx格式的測試計劃领舰。因為之前不太了解jmx文件夫嗓,所以花時間研究了一下,截取jmx文件的前一部分內(nèi)容冲秽,如下圖所示舍咖。
從第一行可以看出,這就是一個xml文件锉桑,我們使用jmeter GUI進(jìn)行操作排霉,其實就是在配置這個xml文件。
一個請求的整體是由標(biāo)簽標(biāo)記的刨仑,我們本次壓測的是java請求郑诺,JavaSampler標(biāo)簽就是標(biāo)記這是一個java請求,即java取樣器杉武。如果壓測的是http請求辙诞,下相應(yīng)的標(biāo)簽為HTTPSamplerProxy。Argument.name和Argument.value分別指定了參數(shù)的名稱和取值轻抱,classname指定了java請求的類名稱飞涂。
在服務(wù)器上進(jìn)行壓測時,需要設(shè)置不同的線程數(shù)祈搜,其他的配置都不需要改動较店,如果每次都拷貝到windows的jmeter GUI中修改再拷貝到服務(wù)器上的話會很麻煩。Jmx文件中容燕,ThreadGroup標(biāo)簽下是線程組的相關(guān)設(shè)置梁呈,其中ThreadGroup.num_threads和LoopController.loops分別對應(yīng)jmeter GUI中線程組頁面中的線程數(shù)和循環(huán)次數(shù),通過vi命令直接在服務(wù)器上修改這兩個參數(shù)即可修改線程組設(shè)置蘸秘。
5在服務(wù)器上跑jmeter壓測腳本
先在jmeter GUI模式下創(chuàng)建測試計劃官卡,保存為jmx文件。把jmeter整個文件夾拷貝到服務(wù)器上醋虏,并進(jìn)入bin目錄下運(yùn)行:
./jmeter.sh -n -t***.jmx-l ***.jtl
-n指定jmeter在非GUI模式下運(yùn)行寻咒,-t指定包含測試計劃的jmx文件名,-l指定jtl日志文件名颈嚼。執(zhí)行完成后會在指定目錄下生成jtl的結(jié)果文件毛秘。
需要注意的是,剛拷貝到linux上的jmeter.sh,用戶是沒有執(zhí)行權(quán)限的叫挟,使用ls –l查看文件詳情艰匙,如下:
需要為該文件添加可執(zhí)行權(quán)限,在jmeter.sh所在目錄下執(zhí)行命令:chmod +x jmeter.sh霞揉,在沒有指定用戶情況下旬薯,默認(rèn)是為所有用戶添加執(zhí)行權(quán)限,再用ls –l查看文件權(quán)限适秩,如下:
還有一點需要注意的是jdk版本,如果測試代碼所用的jdk版本比服務(wù)器上jdk版本高的話會報錯硕舆。
6?性能測試結(jié)果分析
因為接觸性能測試的時間很短秽荞,對系統(tǒng)性能的理解和分析還處于初級的階段,所以此處主要講一下初學(xué)者需要注意的一些點抚官,不涉及具體性能瓶頸的分析扬跋。
在進(jìn)行性能測試之前,需要跟開發(fā)問清楚性能測試的目的凌节,是為了找到系統(tǒng)能支撐的最大負(fù)載還是為了檢查在一定負(fù)載下系統(tǒng)的運(yùn)行情況钦听。如果是后者的話,還需要問一下服務(wù)器端設(shè)置的最大并發(fā)線程數(shù)倍奢,這樣測試時才能設(shè)置出合適的負(fù)載量朴上。我之前做過的幾次http請求的壓測,都是為了找系統(tǒng)能支持的最大負(fù)載卒煞。本次壓測痪宰,我先在本地測試,給了50個線程并循環(huán)100次畔裕,結(jié)果在測試運(yùn)行一段時間后就會出現(xiàn)需要等待很長時間的情況衣撬,一開始以為是本地網(wǎng)絡(luò)狀況不佳,將腳本放在服務(wù)器上跑依然會出現(xiàn)這個情況扮饶,后來跟開發(fā)溝通知道服務(wù)端設(shè)置了最大并發(fā)量的限制具练,并且只需要測試一下特定線程下系統(tǒng)的性能情況即可。有了這次經(jīng)驗之后甜无,在之后的性能測試前就知道要盡量問清楚測試需求了扛点。
還有一點需要注意的是緩存對于性能的影響,有無緩存對性能的影響通常是很大的毫蚓,需要跟開發(fā)確認(rèn)好重點關(guān)注的是有緩存還時沒有緩存的情況占键。本次壓測,有一個測試類是查詢文章subject(話題)屬性的元潘,話題屬性是實時計算出來的畔乙,如果傳入的docid是第一次調(diào)用該方法,返回話題屬性耗時會比較長翩概,之后再對同一個docid查詢話題屬性時是直接從緩存中取數(shù)據(jù)返回牲距,耗時大大減少返咱。對獲取話題屬性的方法進(jìn)行性能測試,在40個線程牍鞠、重復(fù)100次的壓力下咖摹,沒有緩存和有緩存時的TPS分別為70/s和2200/s,可見兩種情況下的差距之大难述。
一般性能測試的時候還需要關(guān)注其他的一些信息萤晴,如cpu和內(nèi)存的使用量,本例中因為線程數(shù)較小胁后,對服務(wù)器cpu和內(nèi)存使用量影響不大店读,所以沒有做詳細(xì)的記錄。
壓測完成后攀芯,需要整理一份較詳細(xì)的結(jié)果反饋給開發(fā)屯断。本次壓測的結(jié)果如下,在沒有緩存的情況下侣诺,TPS較低殖演,并沒有達(dá)到開發(fā)的預(yù)期,需要進(jìn)行性能調(diào)優(yōu)年鸳。