性能测试工具 Jmeter 测试 Dubbo 接口脚本编写

jiong · September 06, 2017 · Last by AnnaWangStar replied at June 21, 2019 · 13449 hits

1、背景
公司大部分的服务都是非HTTP的接口,都是dubbo接口,如今需要对一些接口做性能测试。

2、工具准备
Jmeter 3.2、 Java IDE(本文采用IDEA),Maven作为包管理工具

3、创建一个maven项目,此处可以创建一个quickstart,参考截图

创建好之后,大概的工程结构是这样的:

其中,resource中存放dubbo配置文件。
说到resources,此处有一个坑,dubbo-config.xml中的 http://code.alibabatech.com/schema/dubbo/dubbo.xsd 这个xsd文件,由于code.alibabatech.com 已经停止了服务,需要使用下载一个xsd然后进行本地导入。此处也可以不用下载,直接在pom文件中,配置dubbo的依赖,然后下载dubbo.jar,解压后就会有dubbo.xsd文件,拷贝出来即可,参考下图

pom.xml文件配置

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

<!--jmeter依赖的jar-->
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_core</artifactId>
<version>3.2</version>
<exclusions>
<exclusion>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
</exclusion>
<exclusion>
<groupId>commons-math3</groupId>
<artifactId>commons-math3</artifactId>
</exclusion>
<exclusion>
<groupId>commons-pool2</groupId>
<artifactId>commons-pool2</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- https://mvnrepository.com/artifact/com.alibaba/dubbo -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.3</version>
<exclusions>
<exclusion>
<artifactId>spring</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
</exclusions>
</dependency>

<!-- https://mvnrepository.com/artifact/com.101tec/zkclient -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.3</version>
<exclusions>
<exclusion>
<artifactId>zookeeper</artifactId>
<groupId>org.apache.zookeeper</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.jmeter/ApacheJMeter_java -->
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_java</artifactId>
<version>3.2</version>
<exclusions>
<exclusion>
<groupId>commons-math3</groupId>
<artifactId>commons-math3</artifactId>
</exclusion>
<exclusion>
<groupId>commons-pool2</groupId>
<artifactId>commons-pool2</artifactId>
</exclusion>
</exclusions>
</dependency>

<!--spring依赖的jar-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.36</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>

以上pom文件配置都是一些基本的配置,没有配置要测试的接口的依赖的jar包,实际操作过程中,需要加上。

dubbo-config.xml的配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
"
>

<dubbo:application name="要测试接口的应用名" owner="某人"/>
<dubbo:monitor protocol="registry"/>

<!-- 使用注册中心暴露发现服务地址 -->
<dubbo:registry address="zookeeper://zk的地址"/>

<!-- 生成远程服务代理,可以和本地bean一样使用demoService -->

<dubbo:reference id="iPromiseDubboService"
interface="com.dmall.promise.dubbo.IPromiseDubboService" timeout="5000"
check="
false"/>

</beans>

4、以上配置完毕后,开始撸代码
需要继承Jmeter的AbstractJavaSamplerClient 类,并实现runTest方法

public class QueryTimeInfoBySlotId extends AbstractJavaSamplerClient {
private static final ApplicationContext context = new ClassPathXmlApplicationContext("dubbo-config.xml");

private static IPromiseDubboService iPromiseDubboService;

public void setupTest(JavaSamplerContext arg0){
iPromiseDubboService=(IPromiseDubboService)context.getBean("iPromiseDubboService");
}

public SampleResult runTest(JavaSamplerContext javaSamplerContext) {
SampleResult sr = new SampleResult();
Long timeSlotId=20000l;
try{
sr.sampleStart();
//此处可以增加请求的label,也可以就这样
// sr.setSampleLabel(title);
PromiseRemoteResponse<TimeInfo> responseData=iPromiseDubboService.queryTimeInfoBySlotId(timeSlotId);
if(responseData!=null && "0000".equals(responseData.getCode())){
sr.setSuccessful(true);
sr.setResponseData("code: " + responseData.getCode()+"message: " + responseData.getMessage(),"utf-8");
}else {
sr.setSuccessful(false);
}
sr.sampleEnd();
}catch (Exception e){
e.printStackTrace();
}

return sr;
}
public void teardownTest(JavaSamplerContext arg0){
}
}

5、上面那个类是不需要从jmeter中获取参数,如果要从jmeter 中获取相关的参数,可以参考下面这个类

public class QueryAllOptionalPeriod extends AbstractJavaSamplerClient {

private static final ApplicationContext context = new ClassPathXmlApplicationContext("dubbo-config.xml");

private static IPromiseDubboService iPromiseDubboService;


// 该方法设置的参数,都会出现在jmeter的参数列表中,并且展示相关设置的默认值
public Arguments getDefaultParameters(){
Arguments params = new Arguments();
params.addArgument("title","casetitle");
params.addArgument("erpStoreId", "110");
params.addArgument("latitude", "116.435292");
params.addArgument("longitude","39.994951");
params.addArgument("saleType","1");
return params;
}


public void setupTest(JavaSamplerContext arg0){
iPromiseDubboService=(IPromiseDubboService)context.getBean("iPromiseDubboService");
}

public SampleResult runTest(JavaSamplerContext javaSamplerContext) {
SampleResult sr = new SampleResult();
//从jmeter 中获取相关的参数,组装后,调用相关的接口
long erpStoreId=Long.parseLong(javaSamplerContext.getParameter("erpStoreId"));
Double latitude=Double.parseDouble(javaSamplerContext.getParameter("latitude"));
Double longitude=Double.parseDouble(javaSamplerContext.getParameter("longitude"));
String title=javaSamplerContext.getParameter("title");
Integer saleType=Integer.parseInt(javaSamplerContext.getParameter("saleType"));

System.out.println("param is : tilte is : " + title+"erpStoreId: " + erpStoreId
+ "latitude : "+ latitude + "longitude: " + longitude +"saleType: " +saleType);


PromiseVO promiseVO = new PromiseVO();
promiseVO.setErpStoreId(erpStoreId);
Location location = new Location();
location.setLongitude(longitude);
location.setLatitude(latitude);
promiseVO.setUserLocation(location);

promiseVO.setSaleType(saleType);
try{
sr.sampleStart();
sr.setSampleLabel(title);
PromiseRemoteResponse<List<OptionalPeriod>> responseData=iPromiseDubboService.queryAllOptionalPeriod(promiseVO);
if(responseData!=null && "0000".equals(responseData.getCode())){
sr.setSuccessful(true);
sr.setResponseData("code : " + responseData.getCode() + "message: " + responseData.getMessage(),"utf-8");
}else{
sr.setSuccessful(false);
}
sr.sampleEnd();

}catch (Exception e){
e.printStackTrace();
}


return sr;
}
public void teardownTest(JavaSamplerContext arg0){
}

}

6、调试代码
写完了,固然可以打成一个jar包,然后传到jmeter的lib/ext下面进行调试,但是这样太麻烦了
这里可以直接用main方法调试
当这里main方法调试成功了后,再打jar包,上传到jmeter的相关路径,再进行测试,会高效很多。

public class TestMain {
public static final void main(String [] args){

JavaSamplerContext arg0 = new JavaSamplerContext(new Arguments());

QueryPreSaleOptionalPeriod test=new QueryPreSaleOptionalPeriod();
test.setupTest(arg0);
test.runTest(arg0);

}
}

7、调试通之后,将代码打成jra包
注意,需要将xsd,dubbo-config.xml都要打在jar包里面,需要在pom文件中,还需要配置一些插件

<build>
<plugins>
<!--复制jar包插件,将使用到的jar包,复制到target/lib-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>


<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<id>add-resource</id>
<phase>generate-resources</phase>
<goals>
<goal>add-resource</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>*</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>

<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>TestMain.Main</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>


</plugins>

这样,执行maven package 后,targer下面会有两个东西,是我们需要的:

将此处的jar 文件拷贝到jmeter的 lib/ext里面
然后将lib下面的各种依赖的第三方的jar 拷贝到jmeter的的lib-dependency(自己创建一个就行)

8、因为步骤7 中引入的第三方的jar 包都放在了lib-dependency中,所以需要指定jmeter 启动的时候,加载这个目录下的jar包
修改jmeter.properties 中的 search_paths,配置相关的依赖的jar的路径

9、启动jmeter,创建一个java请求,

10、此处可以把参数放在csv文件里,然后测试各种不同的场景

11、以上做完之后,简单调试下脚本,能够正常运行,然后将相关的依赖,脚本,数据文件传到压测机上,调整线程数,进行正式的压测

本文完
下面是jmeter 在非GUI模式下的分布式测试配置和操作,可以参考。
性能测试过程中,一般是找个linux服务器,用命令行模式进行压测
jmeter non GUI运行分布式压测
前期准备:master 和slave 尽量使用相同的jmeter版本,避免一些奇葩的问题。

配置
slave机器:启动jmeter-server & (后台启动)
master机器:配置remote_host= slave机器的ip
PS: 此处如果master 也要参与压测,需要启动master 机器上的jmeter-server,然后remote_host中需要配置master机器的ip

启动命令,在master机器上执行:
方式一:指定其中一台机器或者多台
sh jmeter.sh -n -t ../../promise-test/testcase10.jmx -R 192.168.90.130
方式二:全部的slave都躁起来压测
sh jmeter.sh -n -t ../../promise-test/testcase10.jmx -r
Attention:注意slave和master 的host配置,一定要有这条host,
本机ip hostname (不可以是127.0.0.1,也不可以是localhsot)
参考URL: http://svn.apache.org/repos/asf/jmeter/tags/v2_4/docs/usermanual/remote-test.html

共收到 7 条回复 时间 点赞

标记一下

—— 来自TesterHome官方 安卓客户端

楼主,请教个问题,我进行完第8步(修改jmeter的search_paths)之后,报错如下:
jmeter.JMeter: An error occurred: java.lang.VerifyError: Cannot inherit from final class

zailushang 从 0 到 1,搭建 dubbo 接口自动化测试 中提及了此贴 27 Oct 16:47

2018/07/16 20:40:14 ERROR - jmeter.gui.GuiPackage: Problem retrieving gui for org.apache.jmeter.protocol.java.control.gui.JavaTestSamplerGui java.lang.NoClassDefFoundError: org/springframework/context/ApplicationContext
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Unknown Source)
at org.apache.jmeter.protocol.java.config.gui.JavaConfigGui.actionPerformed(JavaConfigGui.java:169)
at javax.swing.JComboBox.fireActionEvent(Unknown Source)
at javax.swing.JComboBox.setSelectedItem(Unknown Source)
at javax.swing.JComboBox.setSelectedIndex(Unknown Source)
at org.apache.jmeter.protocol.java.config.gui.JavaConfigGui.clearGui(JavaConfigGui.java:278)
at org.apache.jmeter.protocol.java.control.gui.JavaTestSamplerGui.clearGui(JavaTestSamplerGui.java:97)
at org.apache.jmeter.gui.GuiPackage.createTestElement(GuiPackage.java:339)
at org.apache.jmeter.gui.action.AddToTree.doAction(AddToTree.java:68)
at org.apache.jmeter.gui.action.ActionRouter.performAction(ActionRouter.java:80)
at org.apache.jmeter.gui.action.ActionRouter.access$000(ActionRouter.java:40)
at org.apache.jmeter.gui.action.ActionRouter$1.run(ActionRouter.java:62)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: org.springframework.context.ApplicationContext
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 27 more

2018/07/16 20:40:20 ERROR - jmeter.gui.action.AddToTree: java.lang.RuntimeException: java.lang.NoClassDefFoundError: org/springframework/context/ApplicationContext
at org.apache.jmeter.gui.GuiPackage.createTestElement(GuiPackage.java:350)
at org.apache.jmeter.gui.action.AddToTree.doAction(AddToTree.java:68)
at org.apache.jmeter.gui.action.ActionRouter.performAction(ActionRouter.java:80)
at org.apache.jmeter.gui.action.ActionRouter.access$000(ActionRouter.java:40)
at org.apache.jmeter.gui.action.ActionRouter$1.run(ActionRouter.java:62)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
Caused by: java.lang.NoClassDefFoundError: org/springframework/context/ApplicationContext
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Unknown Source)
at org.apache.jmeter.protocol.java.config.gui.JavaConfigGui.actionPerformed(JavaConfigGui.java:169)
at javax.swing.JComboBox.fireActionEvent(Unknown Source)
at javax.swing.JComboBox.setSelectedItem(Unknown Source)
at javax.swing.JComboBox.setSelectedIndex(Unknown Source)
at org.apache.jmeter.protocol.java.config.gui.JavaConfigGui.clearGui(JavaConfigGui.java:278)
at org.apache.jmeter.protocol.java.control.gui.JavaTestSamplerGui.clearGui(JavaTestSamplerGui.java:97)
at org.apache.jmeter.gui.GuiPackage.createTestElement(GuiPackage.java:339)
... 18 more
Caused by: java.lang.ClassNotFoundException: org.springframework.context.ApplicationContext
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 27 more
安装第8步,创建Java sample时,提示找不到这个类

小李 回复

第8步是配置啊,没有启动,怎么会报错

插件方式生成依赖jar失败,手动导出依赖jar包后,修改jmeter.properties后,jmeter启动不了,怀疑是jar包冲突,想知道你的spring.version

An error occurred: class org.springframework.scheduling.quartz.SimpleTriggerBean has interface org.quartz.SimpleTrigger as super class

回复

已离职原公司,源代码看不到了,不好意思

simple 专栏文章:[精华帖] 社区历年精华帖分类归总 中提及了此贴 13 Dec 14:44

提问一:main调试完后,需要将main方法也打到jar包中吗?
提问二:在jmeter中添加Java请求遇到问题:

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up