最近需要对改造的redis缓存接口做压力测试,使用了开源压力测试工具JMeter,分享一下自己的使用经验,希望能对需要进行压力测试的开发同学有所帮助。
JMeter介绍
JMeter是Apache软件基金会下的一款开源压力测试工具,官方网址是:http://jmeter.apache.org/。JMeter可以测试静态、动态资源的性能,这些资源包括文件、Servlets 、Perl脚本、Java对象、数据库、FTP服务器等,并生成图形报告。JMeter使用Java开发,既支持可视化界面操作,也支持命令行操作。
Java请求测试(界面操作)
由于需要对改造的redis缓存接口测试,因此使用了JMeter的Java请求测试,安装和使用步骤如下所示(以Windows操作系统为例,并默认已安装、配置Java运行环境)。
1)从官网上下载JMeter并解压,例如解压至C:\apache-jmeter-2.9。
2)配置JMeter环境变量,增加变量JMETER_HOME,值为“C:\apache-jmeter-2.9”,修改变量CLASSPATH,增加“%JMETER_HOME%\lib\ext\ApacheJMeter_core.jar;% JMETER_HOME%\lib\jorphan.jar;%JMETER_HOME%\lib\logkit-1.2.jar;”
3)执行JMeter目录下的bin\jmeter.bat,显示JMeter界面说明安装成功。
4)新建Java工程,在该工程中,引入JMeter目录下lib中的jar包,继承JMeter的AbstractJavaSamplerClient类,在子类中重写setupTest、teardownTest、getDefaultParameters、runTest方法,实现对缓存接口的get、set、hGet、hSet方法进行测试,子类代码如下所示。
package com.sohu.cms.test; 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.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.sohu.cms.datacache.DataCache; import com.sohu.cms.datacache.DataCacheManager; /** * 继承jmeter的AbstractJavaSamplerClient类, * 重写setupTest、teardownTest、getDefaultParameters、runTest方法 * 对缓存接口的get、set、hGet、hSet方法进行测试 * @author wang * */ public class TestDataCacheClient extends AbstractJavaSamplerClient{ private static long start = 0; private static long end = 0; private static DataCacheManager dataCacheManager; private static DataCache dataCache; private static int size = 8192; private static String method = "get"; private static Object key = "TEST_KEY"; private static Object field = "TEST_FIELD"; private static Object value = new Byte[size]; //此处初始化我们改造的redis缓存接口dataCache static { ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); dataCacheManager = (DataCacheManager) context.getBean("dataCacheManager"); dataCache = dataCacheManager.getObjectDataCache(); } /** * 测试开始 */ public void setupTest(JavaSamplerContext arg0) { start = System.currentTimeMillis(); } /** * 测试结束 */ public void teardownTest(JavaSamplerContext arg0) { end = System.currentTimeMillis(); System.out.println("[method=" + method + "] [size=" + size + "] [time:" + (end - start) / 1000); } /** * 添加参数默认值,参数默认值会在界面中显示 */ public Arguments getDefaultParameters() { //添加两个参数, //size为测试value值的字节大小,默认为8192字节 //method为需要进行测试的缓存接口方法,默认为get Arguments args = new Arguments(); args.addArgument("size", "8192"); args.addArgument("method","get"); return args; } /** * 测试 */ public SampleResult runTest(JavaSamplerContext arg0) { //获取界面或测试计划配置文件(jmx)中传入的参数 if (arg0.getParameter("method") != null) method = arg0.getParameter("method"); if (arg0.getIntParameter("size") > 0) size = arg0.getIntParameter("size"); value = new Byte[size]; SampleResult sr = new SampleResult(); try { boolean result = true; //根据传入的method参数测试相应的缓存接口方法 if (method.equals("get")){ //测试用例的核心逻辑 //测试用例开始 sr.sampleStart(); //测试用例执行 result = get(); //测试用例结果 sr.setSuccessful(result); //测试用例结束 sr.sampleEnd(); } else if (method.equals("set")) { sr.sampleStart(); result = set(); sr.setSuccessful(result); sr.sampleEnd(); } else if (method.equals("hGet")) { sr.sampleStart(); result = hGet(); sr.setSuccessful(result); sr.sampleEnd(); } else if (method.equals("hSet")) { sr.sampleStart(); result = hSet(); sr.setSuccessful(result); sr.sampleEnd(); } } catch (Exception e) { e.printStackTrace(); } return sr; } /** * 缓存接口get方法测试用例 * @return */ public boolean get() { boolean result = true; try { dataCache.get(key); } catch (Exception e) { System.out.println(e); result = false; } return result; } /** * 缓存接口set方法测试用例 * @return */ public boolean set() { boolean result = true; try { dataCache.put(key, value); } catch (Exception e) { System.out.println(e); result = false; } return result; } /** * 缓存接口hGet方法测试用例 * @return */ public boolean hGet() { boolean result = true; try { dataCache.hGet(key, field); } catch (Exception e) { System.out.println(e); result = false; } return result; } /** * 缓存接口hSet方法测试用例 * @return */ public boolean hSet() { boolean result = true; try { result = dataCache.hSet(key, field, value); } catch (Exception e) { System.out.println(e); result = false; } return result; } }
5)将工程以jar包形式导出,置于JMeter目录lib\ext下,并将工程依赖的jar包置于JMeter目录lib下,启动JMeter。右键“测试计划”添加“线程组”。右键“线程组”添加“Java请求”,右键“Java请求”添加“聚合报告”。
6)在“Java请求”中选择所写的测试类,并可以设置相关参数,在“线程组”中可以设置并发线程数和循环次数,点击上方“启动”按钮开始测试,测试完成后,可以在“聚合报告”中查看到测试结果。
Java请求测试(命令行操作)
JMeter也支持命令行操作,在服务器上进行测试时,我们就采用了这种方式。一个简单的JMeter命令行操作如下所示(在JMeter目录bin下执行)。
Windows下: JMeter-n �Ct test.jmx -l result.jtl Linux下: ./jmeter.sh -n �Ct test.jmx -l result.jtl |
其中-n 表示命令行执行,test.jmx为测试计划配置文件,result.jtl为测试结果。
可在界面中进行测试计划配置,然后保存便可生成jmx格式文件,如下所示。
<?xml version="1.0" encoding="UTF-8"?> <jmeterTestPlan version="1.2" properties="2.4" jmeter="2.9 r1437961"> <hashTree> <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="测试计划" enabled="true"> <stringProp name="TestPlan.comments"></stringProp> <boolProp name="TestPlan.functional_mode">false</boolProp> <boolProp name="TestPlan.serialize_threadgroups">false</boolProp> <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true"> <collectionProp name="Arguments.arguments"/> </elementProp> <stringProp name="TestPlan.user_define_classpath"></stringProp> </TestPlan> <hashTree> <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="线程组" enabled="true"> <stringProp name="ThreadGroup.on_sample_error">continue</stringProp> <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="循环控制器" enabled="true"> <boolProp name="LoopController.continue_forever">false</boolProp> <!-- 循环次数 --> <stringProp name="LoopController.loops">100</stringProp> </elementProp> <!-- 并发线程数 --> <stringProp name="ThreadGroup.num_threads">100</stringProp> <stringProp name="ThreadGroup.ramp_time">100</stringProp> <longProp name="ThreadGroup.start_time">1376103961000</longProp> <longProp name="ThreadGroup.end_time">1376103961000</longProp> <boolProp name="ThreadGroup.scheduler">false</boolProp> <stringProp name="ThreadGroup.duration"></stringProp> <stringProp name="ThreadGroup.delay"></stringProp> </ThreadGroup> <hashTree> <JavaSampler guiclass="JavaTestSamplerGui" testclass="JavaSampler" testname="Java请求" enabled="true"> <elementProp name="arguments" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" enabled="true"> <!-- 自定义参数 --> <collectionProp name="Arguments.arguments"> <elementProp name="size" elementType="Argument"> <stringProp name="Argument.name">size</stringProp> <stringProp name="Argument.value">8192</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="method" elementType="Argument"> <stringProp name="Argument.name">method</stringProp> <stringProp name="Argument.value">set</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> </collectionProp> </elementProp> <stringProp name="classname">com.sohu.cms.test.TestDataCacheClient</stringProp> </JavaSampler> <hashTree> <ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="聚合报告" enabled="true"> <boolProp name="ResultCollector.error_logging">false</boolProp> <objProp> <name>saveConfig</name> <value class="SampleSaveConfiguration"> <time>true</time> <latency>true</latency> <timestamp>true</timestamp> <success>true</success> <label>true</label> <code>true</code> <message>true</message> <threadName>true</threadName> <dataType>true</dataType> <encoding>false</encoding> <assertions>true</assertions> <subresults>true</subresults> <responseData>false</responseData> <samplerData>false</samplerData> <xml>false</xml> <fieldNames>false</fieldNames> <responseHeaders>false</responseHeaders> <requestHeaders>false</requestHeaders> <responseDataOnError>false</responseDataOnError> <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage> <assertionsResultsToSave>0</assertionsResultsToSave> <bytes>true</bytes> </value> </objProp> <stringProp name="filename"></stringProp> </ResultCollector> <hashTree/> </hashTree> </hashTree> </hashTree> </hashTree> </jmeterTestPlan>
result.ftl为按行输出的测试执行结果,如下所示,可在界面操作的聚合报告导入该文件从而生成聚合报告。
... 1376040889436,48,Java请求,,,线程组1-40,,true,0,0 1376040889792,9,Java请求,,,线程组1-7,,true,0,0 1376040889526,167,Java请求,,,线程组1-57,,true,0,0 1376040889791,10,Java请求,,,线程组 1-54,,true,0,0 ... |