1.Jmeter 概要描敘
jmeter 是一款專門用於功能測試和壓力測試的輕量級測試開發平臺。多數情況下是用作壓力測試,該測試工具在阿里巴巴有着廣泛的使用,估計是不要錢吧,哈哈,功能上來說,整個平臺實現了許多和互聯網相關的網絡測試組件,同時還保留着很強的擴展性方便用於一些非標準的測試場景,讓偉大的程序猿同學來告訴它該如何工作。下面我們開始。
2.Jmeter 基本界面和組件的概念
現在看到了Jmeter的主界面。下面介紹Jmeter中主要組件的功能和作用, 首先我們看看Jmeter在概念上的組件分類如圖
1.Threads:這個組件主要用來控制Jmeter併發時產生線程的數量,在它的下一級菜單下只有一個組件(線程組),可以這麼理解每個線程就是一個虛擬的用戶。所有的其他類型組件必須是(線程組)節點的子節點。
2.配置單元:和Sample組件一起工作,主要用來配置Sample如何來發起請求訪問服務器,這個東西的主要特點是可以把一些Sample的共同配置放在一個元素裏面方便管理,配置單元是有作用域的。作用域和樹的那個關係一樣越是上級節點的作用域越大,越是接近葉子節點的
作用域就越小,可以複寫上級作用域的配置。
3.定時器 : 這個主要是用來調節(線程組),控制線程每次運行測試邏輯(比如說:發出請求)的時間間隔。當然這個下面還有很多類型的定時器,他們主要功能就是調節時間間隔,但個個組件之間的策略有很大不同。
4.前置處理器 和 後置處理器類似一個HOOK,在測試執行之前和執行之後執行一些腳本的邏輯。該組件我還沒有具體使用過,但大致功能就是這樣,非重點組件。
5.Sample : 可能上圖中沒有出現Sample,需要在(ThreadGroup)上添加纔可以,見圖
Sample表示客戶端發送某種格式或者規範的請求到服務端,所以大家看到了各種各樣的Sample,其中有兩個Http 相關的。一般用HttpClient功能和效率將更強。
6.斷言: 意思是指對於Sample完成了請求發送之後,判斷一下返回的結果是否滿足期望。
7.監聽器 : 這個組件不同於平時在Web編程的那種監聽器,他是伴隨着Jemeter測試的運行而從中抓取運行期間的數據的一個組件,經常使用的是聚合報告組件,從裏面可以統計到測試的TPS,響應時間等關鍵測試數據。
3.實例操作
首先在TestPlan下面添加一個ThreadGroup組件,設置線程組組件各項參數
關鍵參數解釋 :
線程數:最大測試時使用的線程數。
Ramp-Up Period : Jmeter達到指定最大線程數的時間。
循環次數 : 如果是Forever,線程組中的線程將不間斷的連續測試系統,當然也可以設置每個線程測試的次數,當完成了規定次數後,該線程將自動退出線程組。
調度器 : 主要用來指定該測試的一些時間信息,比如從幾點到幾點運行測試,如果到了指定時間測試沒有進行完成,測試也會被停止。
接着在線程組下面添加Sample組件,我們添加一個HTTP Request HTTPClient組件,設置屬性如下圖:
最後添加監聽器組件 :Aggregate Report, 大功告成!
運行:
好了,一個簡單的壓力測試例子完成了。大家可以從report中間看到一些性能結果的參數了。下面我們完成一個更復雜的例子。
4.自定義Sample或者協議完成測試
問題:在某些場景下我們會發現Jmeter裏面提供的各種Sample不能滿足自己的需求,應爲這個世界上的壓力測試的邏輯本來就是千變萬化的,所以這個時候我們如果自己實現一套測試邏輯,這個時候需要使用jmeter 的擴展性。下面我們通過一個簡單的例子來看看如何開發這樣的一個自定義Sample? 自定Sample的生命週期是怎樣的?
- package com.alibaba.stress.load;
- import org.apache.jmeter.config.Arguments;
- import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
- import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
- import org.apache.jmeter.samplers.SampleResult;
- public class LifecycleJMeterSampleextends AbstractJavaSamplerClient {
- @Override
- public Arguments getDefaultParameters() {
- System.out.println("Get Parameter name! [getDefaultParameters]");
- return super.getDefaultParameters();
- }
- @Override
- public void setupTest(JavaSamplerContext context) {
- System.out.println("[setupTest]");
- super.setupTest(context);
- }
- @Override
- public void teardownTest(JavaSamplerContext context) {
- System.out.println("[teardownTest]");
- super.teardownTest(context);
- }
- @Override
- public SampleResult runTest(JavaSamplerContext ctx) {
- SampleResult result = new SampleResult();
- result.sampleStart();
- System.out.println("[runTest]");
- result.setSuccessful(true);
- result.sampleEnd();
- return result;
- }
- }
- package com.alibaba.stress.load;
- import org.apache.jmeter.config.Arguments;
- import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
- import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
- import org.apache.jmeter.samplers.SampleResult;
- public class LifecycleJMeterSample extends AbstractJavaSamplerClient {
- @Override
- public Arguments getDefaultParameters() {
- System.out.println("Get Parameter name! [getDefaultParameters]");
- return super.getDefaultParameters();
- }
- @Override
- public void setupTest(JavaSamplerContext context) {
- System.out.println("[setupTest]");
- super.setupTest(context);
- }
- @Override
- public void teardownTest(JavaSamplerContext context) {
- System.out.println("[teardownTest]");
- super.teardownTest(context);
- }
- @Override
- public SampleResult runTest(JavaSamplerContext ctx) {
- SampleResult result = new SampleResult();
- result.sampleStart();
- System.out.println("[runTest]");
- result.setSuccessful(true);
- result.sampleEnd();
- return result;
- }
- }
- package com.alibaba.stress.load;
- import org.apache.jmeter.config.Arguments;
- import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
- import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
- import org.apache.jmeter.samplers.SampleResult;
- public class LifecycleJMeterSample extends AbstractJavaSamplerClient {
- @Override
- public Arguments getDefaultParameters() {
- System.out.println("Get Parameter name! [getDefaultParameters]");
- return super.getDefaultParameters();
- }
- @Override
- public void setupTest(JavaSamplerContext context) {
- System.out.println("[setupTest]");
- super.setupTest(context);
- }
- @Override
- public void teardownTest(JavaSamplerContext context) {
- System.out.println("[teardownTest]");
- super.teardownTest(context);
- }
- @Override
- public SampleResult runTest(JavaSamplerContext ctx) {
- SampleResult result = new SampleResult();
- result.sampleStart();
- System.out.println("[runTest]");
- result.setSuccessful(true);
- result.sampleEnd();
- return result;
- }
- }
其中AbstractJavaSamplerClient是從ApacheJmeter_Java.jar 中獲得。編寫的類文件打包xxx.jar, 把這個Jar放到Jmeter的解壓目錄下的lib/ext下。重新啓動jmeter.
一次先加入線程組,和聚合報告兩個組件,在聚合報告組件之前加入一個新的Sample(Java Sample ), 如圖:
運行測試。可以看到控制檯的輸出。調整幾次線程組的線程數目的控制,我們會發現我們的這個自定義類中的teardowntest 和 setupuptest方法都是正對線程組中的某一個線程來執行的,線程組中線程的調用數目其實就是指runtest方法的運行次數。
5.Jmeter的分佈式測試(優缺點)
下面是分佈式測試的使用方法,即使用一臺機器控制多臺機器給目標機器產生壓力。由於Linux和Windows上的方法有些不一樣,這裏說下Linux下是怎麼來配置的。
首先總控的機器我們叫master,產生壓力的肉雞我們叫JmeterServer,待我們測試的系統我們叫Target.
第一步:啓動JmeterServer。我們可以找到Jmeter/bin 目錄下的jmeter-server這個腳本,運行即可,當然如果你需要調整這個測試負載產生服務器的內存等性能參數的話,我們修改這個腳本jmeter中的內存參數。
第二步 : 修改Master機器上jmeter/bin目錄下的jmeter.properties文件,修改remote_hosts=localhost:1099,localhost:2010這個爲你自己的JmeterServer的實際IP,如果有多臺可以用逗號分開。
第三步 : 啓動master jmeter,運行測試腳本的時候選:運行 --> 遠程運行 --> 選擇IP 或者 運行所有遠程。JmeterServer就會運行Master機器上設定的運行腳本了。
問題 : 我在實際使用中發現使用這種模式下的Jmeter在Master觀測實際測試結果,發現TPS遠遠低於使用傳統的一臺Jmeter來測試所能達到的TPS,我感覺可能是由於Jmeter這種多機器協作中傳遞信息是使用RMI技術,可能這種技術本身還對Jmeter的性能產生了影響導致壓力上去的很慢。該問題還沒有得到完全認證,所以我現在如果在一臺機器測試產生的壓力不夠的情況下我直接使用多臺機器測把TPS加起來就是結果,比較傻,期待大俠指點迷津