最近工作中的一個項目要求做效能測試,該項目由提供服務的幾個應用組成,選用的架構是阿里巴巴公司開源的服務架構Dubbo。關於Dubbo的介紹,網上也有很多資料,本人只是做了粗略的瞭解,沒有深入研究,相關資料地址如下:http://www.iteye.com/magazines/103,http://alibaba.github.io/dubbo-doc-static/User+Guide-zh.htm#UserGuide-zh-%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A,http://www.oschina.net/p/dubbo, 應用執行個體(含源碼):http://blog.csdn.net/wilsonke/article/details/39896595,http://download.csdn.net/detail/u012049463/6338227
Jmeter(官網:http://jmeter.apache.org/download_jmeter.cgi)做為一款開源的效能測試工具,提供了詳細的使用者操作手冊、demo以及api文檔,一直都是我們做效能測試的首選,不過之前大多做的都是Http請求的測試,對介面形式的Java請求測試並沒有實踐過。最快的學習方法就是從網上找例子,依葫蘆畫瓢就成,的確我也是這麼乾的,雖然網上類似的文章也很多,不過這裡還是想對自己的實踐過程做個記錄。^^ 二. 本文: 1. 怎麼寫Jmeter介面測試的java測試類別
package com.huangjie.dubbo_Service.service.impl;import com.huangjie.dubbo_Service.service.IProcessData;public class ProcessDataImpl implements IProcessData { /* * @see com.xxx.bubbo.provider.IProcessData#deal(java.lang.String) */ @Override public String setAge(String age) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "You are " + age + "years old!"; } @Override public String sayHi(String name) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "Hi:" + name; } } 用戶端JavaSampler測試案例類
package com.huangjie.dubbo_Consumer;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;import org.apache.log.Logger;import org.springframework.context.support.ClassPathXmlApplicationContext;import com.huangjie.dubbo_Service.service.IProcessData;public class JmeterInterfaceTest extends AbstractJavaSamplerClient{ private static String label_name = "dubbo_consumer";//定義label名稱,顯示在jmeter的結果視窗 private static final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath*:applicationConsumer.xml"); private static IProcessData demoService = null; private Logger log = getLogger(); @Override public void setupTest(JavaSamplerContext arg0) { log.info("測試案例開始執行..."); //定義測試初始值,setupTest只在測試開始前使用 demoService = (IProcessData) context.getBean("demoService"); } @Override public Arguments getDefaultParameters() { //參數定義,顯示在前台,也可以不定義 Arguments params = new Arguments(); params.addArgument("name", "Tom"); params.addArgument("age", "23"); return params; } @Override public SampleResult runTest(JavaSamplerContext arg0) { boolean success = true; SampleResult sr = new SampleResult(); sr.setSampleLabel(label_name); sr.sampleStart();//用來統計執行時間--start-- try { String name = arg0.getParameter("name"); String msg = demoService.sayHi(name); Thread.sleep(5000); System.out.println(msg); sr.setResponseMessage(msg); sr.setResponseCode("1000"); } catch (Exception e) { success = false; }finally{ sr.sampleEnd();//用來統計執行時間--end-- sr.setSuccessful(success); } return sr; } @Override public void teardownTest(JavaSamplerContext arg0){ log.info("測試案例執行結束..."); }} 說明:
首先,getDefaultParameters()方法是用來定義參數的,這裡我們定義了name和age兩個參數並賦了預設值”Tom”和“23”,這樣在jmeter建立的java請求中選擇上面的測試類別就能看到這兩個參數了,並且參數值也是可以任意修改的。
接著,仔細看會發現,測試類別裡面我們只調用了sayHi()這個方法,並沒有調用服務端提供的setAge()方法,之所以不在一個測試案例裡面去調用兩個測試方法是因為我們這裡的目的是去測試單一的情境,也就是如果要分別測這兩個方法那就得再建一個測試案例類,其它部分的代碼都可以不變,只要把實現方法替換一下就可以了。因此,我們會想,如果一個介面提供n種方法,那麼我們就要寫類似的n個類,雖說複製粘貼很容易,但這樣就會產生一堆類檔案,感覺很冗餘,這裡可以用Java的反射機制來實現方法的動態調用。簡單的說就是,在runTest()方法中不寫死具體用例調用的方法,而是由Java反射機制通過輸入參數來決定。改寫上面的測試案例類如下:
先寫一個抽象類別來繼承AbstractJavaSamplerClient,裡面唯寫動態調用的方法,其它方法還是在用例類裡面重寫。
package com.huangjie.dubbo_Consumer;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;import org.apache.jmeter.samplers.SampleResult;public abstract class AbstractServiceClient extends AbstractJavaSamplerClient{ public Object invokeRunTest(String testName, JavaSamplerContext context , SampleResult sampleResult){ Method[] methods = this.getClass().getMethods(); for (Method method : methods) { if (method.getName().equalsIgnoreCase(testName)) { try { return method.invoke(this, context, sampleResult); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } return null; } } 測試案例類繼承上面的動態方法引動過程的抽象類別
package com.huangjie.dubbo_Consumer;import org.apache.jmeter.config.Arguments;import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;import org.apache.jmeter.samplers.SampleResult;import org.springframework.context.support.ClassPathXmlApplicationContext;import com.huangjie.dubbo_Service.service.IProcessData;public class DynamicMethodInvokeTest extends AbstractServiceClient{ private static String label_name = "dubbo_consumer";//定義label名稱,顯示在jmeter的結果視窗 private static final ClassPathXmlApplicationContext cxt = new ClassPathXmlApplicationContext("classpath*:applicationConsumer.xml"); private static IProcessData demoService = null; @Override public void setupTest(JavaSamplerContext context) { //定義測試初始值,setupTest只在測試開始前使用 super.setupTest(context); demoService = (IProcessData) cxt.getBean("demoService"); } @Override public Arguments getDefaultParameters() { //參數定義,顯示在前台,也可以不定義 Arguments params = new Arguments(); params.addArgument("name", "Tom"); params.addArgument("age", "20"); params.addArgument("testName", "setAge"); return params; } @Override public SampleResult runTest(JavaSamplerContext context) { String testName = context.getParameter("testName"); SampleResult sr = new SampleResult(); super.invokeRunTest(testName, context, sr); return sr; } public void setAge(JavaSamplerContext context, SampleResult sr){ boolean success = true; sr.setSampleLabel("setAge"); sr.sampleStart(); try { String age = context.getParameter("age"); String msg = demoService.setAge(age); Thread.sleep(5000); System.out.println(msg); sr.setResponseMessage(msg); sr.setResponseCode("1000"); } catch (Exception e) { success = false; }finally{ sr.sampleEnd(); sr.setSuccessful(success); } } public void sayHi(JavaSamplerContext context, SampleResult sr){ boolean success = true; sr.setSampleLabel("sayHi"); sr.sampleStart(); try { String name = context.getParameter("name"); String msg = demoService.sayHi("Hi,"+name); Thread.sleep(5000); System.out.println(msg); sr.setResponseMessage(msg); sr.setResponseCode("1001"); } catch (Exception e) { success = false; }finally{ sr.sampleEnd(); sr.setSuccessful(success); } } @Override public void teardownTest(JavaSamplerContext context){ super.teardownTest(context); }} 這裡我們新增了一個參數testName,通過這個參數來決定具體調用哪個用例方法。這樣,我們就實現了在同一個類裡面寫不同的測試案例了,感覺優雅多了,哈哈。 二. Jmeter中建立Java Sampler Test Plan
想要在Jmeter中執行上面寫的測試案例,有以下幾個步驟:
1. 把項目打成jar包,方法1用eclipse或者myeclipse的export直接匯出jar包,方法2用maven的package或這install指令打包,這裡建議用maven來管理項目,打包方便並且不會出錯,我試過用IDE直接export出jar包,但執行的過程中報找不到spring的bean設定檔,用maven打包沒有出現這種情況。
2.將打好的jar包放到Jmeter的lib/ext路徑下,運行Jmeter,在建立的Java請求Sampler中就能找到自己寫的測試案例類了。
3.把項目依賴的所有jar包拷貝到Jmeter的lib路徑下,因為跑自己的測試案例可能用到這些jar中的類,否則執行過程中會報找不到class。如果是maven項目,則可以通過執行指令mvn dependency:copy-dependencies 把項目中所有依賴的jar匯出到項目的target/dependency檔案夾中,然後拷貝到Jmeter的lib下。
4.運行Jmeter按照具體的效能指標和情境,開始建立你的測試計劃吧。