自学内容网 自学内容网

JMeter Java请求开发方法

Jmeter支持多种请求方式,这里要介绍的是通过编写java实现的Java请求方式。

http请求适合大部分场景,用户能够自定义报文头、报文体、Content-Type等信息。但对于自定义拆组包,或报文带有加解密功能的情况,此时使用http请求可能就不那么容易了(可能通过beanshell也能实现,没有研究)。对于java开发工程师来说,用java的方式来编写自然是首选方案了,下面介绍如何用java来开发jmeter的java请求。

Java请求快速开始

新建maven工程

    <groupId>org.example</groupId>
    <artifactId>hello-jmeter</artifactId>
    <version>1.0-SNAPSHOT</version>

hello world

没在官网找到java请求的开发教程,直接百度了:

Jmeter 使用-JAVA请求 - 冰蓝小猪宝宝 - 博客园

参考这个,写个hello world:

添加依赖

1.在工程根目录下,创建lib目录,将apache-jmeter-5.6.3/lib/ext目录下的ApacheJMeter_core.jar文件和ApacheJMeter_java.jar文件复制到lib目录

2.在maven中添加system依赖:

        <dependency>
            <groupId>org.apache.jmeter</groupId>
            <artifactId>ApacheJMeter_core</artifactId>
            <version>1.0</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/lib/ApacheJMeter_core.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>org.apache.jmeter</groupId>
            <artifactId>ApacheJMeter_java</artifactId>
            <version>1.0</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/lib/ApacheJMeter_java.jar</systemPath>
        </dependency>

这里的groupId和artifactId是自定义的,因为他是从本地路径读的。

编写Java测试类

编写HelloJavaSample测试类

public class HelloJavaSample extends AbstractJavaSamplerClient {
    private AtomicInteger counter = new AtomicInteger(0);
    @Override
    public void setupTest(JavaSamplerContext context) {
        super.setupTest(context);
    }

    @Override
    public void teardownTest(JavaSamplerContext context) {
        super.teardownTest(context);
    }

    public SampleResult runTest(JavaSamplerContext javaSamplerContext) {
        System.out.println("hello world!"+counter.incrementAndGet());
        SampleResult sampleResult = new SampleResult();
        sampleResult.sampleStart();
        try {
            Thread.sleep(1);
            sampleResult.setSuccessful(true);
        } catch (InterruptedException e) {
            e.printStackTrace();
            sampleResult.setSuccessful(false);
        }
        sampleResult.sampleEnd();
        sampleResult.setResponseCode("200");
        sampleResult.setResponseMessage("success!");
        sampleResult.setSamplerData("hello");
        sampleResult.setResponseData("hello world!".getBytes());
        return sampleResult;
    }
}

打包maven工程

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.0.2</version>
                <configuration>
                    <outputDirectory>${project.basedir}/result</outputDirectory>
                </configuration>
            </plugin>

修改jmeter.properties文件

修改bin/jmeter.properties文件,搜索search_paths关键字:

# List of directories (separated by ;) to search for additional JMeter plugin classes,
# for example new GUI elements and samplers.
# Any jar file in such a directory will be automatically included,
# jar files in sub directories are ignored.
# The given value is in addition to any jars found in the lib/ext directory.
# Do not use this for utility or plugin dependency jars.
#search_paths=/app1/lib;/app2/lib
search_paths=F:/my-practice/backend/hello-jmeter/result

这里的路径按自己的真实路径填写。

启动jmeter

点击右侧下拉选择,可以看到我们刚添加的HelloJavaSample类,选中。

添加必要的监听器 单笔测试

可以看到运行了1次测试,tps是1000,也能看到请求响应报文。

另外,观察控制台输出,可以看到每次都是1.也就是说每次jmeter都相当于new了一个你的java类,来进行测试的,准确的说是每个线程在整个运行过程中,使用同一个sample对象。

正式开发

以添加fastjson和hutools为例,来演示如何添加依赖包,以及添加依赖包后如何打包。

1.添加依赖

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.4</version>
        </dependency>

2.开发代码

public class HelloJsonSample extends AbstractJavaSamplerClient {
    private AtomicInteger counter = new AtomicInteger(0);
    private JSONObject template = new JSONObject();
    private String url = "http://localhost:8081/post";
    @Override
    public void setupTest(JavaSamplerContext context) {
        template.put("name", "HelloJsonSample");
        template.put("version", "1.0.0");
    }

    @Override
    public void teardownTest(JavaSamplerContext context) {
        super.teardownTest(context);
    }

    public SampleResult runTest(JavaSamplerContext javaSamplerContext) {
        int count = counter.incrementAndGet();
        template.put("count", count);
        SampleResult sampleResult = new SampleResult();
        HttpResponse httpResponse = null;
        String body = template.toJSONString();
        sampleResult.setSamplerData(body);
        try {
            sampleResult.sampleStart();
            httpResponse = HttpRequest.post(url).body(body).timeout(60000).execute();
            sampleResult.sampleEnd();
        } catch (Exception e) {
            e.printStackTrace();
            sampleResult.sampleEnd();
            sampleResult.setSuccessful(false);
            sampleResult.setResponseMessage(e.getMessage());
            return sampleResult;
        }
        String resBody = httpResponse.body();
        // 解析返回报文
        JSONObject resJson = JSON.parseObject(resBody);
        String code = resJson.getString("code");
        String retMsg = resJson.getString("retMsg");
        sampleResult.setResponseCode(code);
        sampleResult.setResponseMessage(retMsg);
        sampleResult.setResponseData(httpResponse.bodyBytes());
        sampleResult.setSentBytes(httpResponse.bodyBytes().length);
        if(!Objects.equals("000000", code)) {
            sampleResult.setSuccessful(false);
        } else {
            sampleResult.setSuccessful(true);
        }
        return sampleResult;
    }
}

这里有几点需要说明一下:

a.sampleResult.sampleStart()和sampleResult.sampleEnd()方法,不是在最外面的,而是在http调用的地方,这样统计出的响应时间更准确。

b.成功失败根据返回报文解析后的结果判断,这也符合业务需要

c.这里构造了一个template对象,这样每次迭代可以复用这个对象,公共请求信息不用每次添加。

3.复制依赖配置

将依赖也打包到${project.basedir}/result目录,同时排除掉jmeter的依赖(因为jmeter默认加载)。

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.0.2</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.basedir}/result</outputDirectory>
                            <excludeGroupIds>org.apache.jmeter</excludeGroupIds>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

这段配置是为了自动复制工程依赖到result目录下,同时忽略掉jmeter那两个systemPath的依赖。

4.准备模拟服务端

用springboot启动一个简易的http服务端。

    @PostMapping("/post")
    public JSONObject post(@RequestBody Map<String, String> map){
        JSONObject json = new JSONObject();
        json.put("code", "000000");
        json.put("retMsg", "success");
        json.put("origin", map);
        log.info("接收到报文:{}", map);
        return json;
    }

修改对应的url地址。

5.打包并测试

打包前先停掉jmeter。打包结果为:

重新启动jmeter,添加新的线程组、添加java Request。

结果证明报文正确的发送到了服务端,并收到了响应。

6.开50个线程跑10秒性能

tps达到了24420,可以认为java请求的方式本身没有性能瓶颈,能够作为压测的手段。tps可以更高,因为我的模拟服务端是本地的springboot服务,往console控制台打日志了,对效率也有影响。

java请求参数

java请求参数

在java请求页面,可以看到发送参数,下面演示如何个性化设置发送参数。

添加测试类

public class HelloJsonArgSample extends AbstractJavaSamplerClient {
    private AtomicInteger counter = new AtomicInteger(0);
    private JSONObject template = new JSONObject();
    @Override
    public void setupTest(JavaSamplerContext context) {
        template.put("name", "HelloJsonSample");
        template.put("version", "1.0.0");
    }

    @Override
    public Arguments getDefaultParameters() {
        Arguments arguments = new Arguments();
        arguments.addArgument("url", "http://localhost:8081/post");
        arguments.addArgument("key1", "value1");
        return arguments;
    }

    @Override
    public void teardownTest(JavaSamplerContext context) {
        super.teardownTest(context);
    }

    public SampleResult runTest(JavaSamplerContext javaSamplerContext) {
        int count = counter.incrementAndGet();
        template.put("count", count);
        String val = javaSamplerContext.getParameter("key1");
        template.put("key1", val);
        SampleResult sampleResult = new SampleResult();
        HttpResponse httpResponse = null;
        String body = template.toJSONString();
        String url = javaSamplerContext.getParameter("url");
        sampleResult.setSamplerData(body);
        try {
            sampleResult.sampleStart();
            httpResponse = HttpRequest.post(url).body(body).timeout(60000).execute();
            sampleResult.sampleEnd();
        } catch (Exception e) {
            e.printStackTrace();
            sampleResult.sampleEnd();
            sampleResult.setSuccessful(false);
            sampleResult.setResponseMessage(e.getMessage());
            return sampleResult;
        }
        String resBody = httpResponse.body();
        // 解析返回报文
        JSONObject resJson = JSON.parseObject(resBody);
        String code = resJson.getString("code");
        String retMsg = resJson.getString("retMsg");
        sampleResult.setResponseCode(code);
        sampleResult.setResponseMessage(retMsg);
        sampleResult.setResponseData(httpResponse.bodyBytes());
        sampleResult.setSentBytes(httpResponse.bodyBytes().length);
        if(!Objects.equals("000000", code)) {
            sampleResult.setSuccessful(false);
        } else {
            sampleResult.setSuccessful(true);
        }
        return sampleResult;
    }
}

a.重载了getDefaultParameters方法

b.修改了runTest中url的获取方式

重新打包测试

关闭jmeter,重新打包,启动jmeter,添加新的线程组和java request。

可以看到,那两个参数能够在界面上看见了。

发送一笔请求,测试正常。

动态修改参数

这里的参数支持动态修改:

将value1改成value2:

但当我们切换java类时,他会自动还原。这样方便我们不修改java代码,动态调整基础参数。

CSV参数文件读取

读取csv文件测试

编写测试类

public class HelloJsonCsvSample extends AbstractJavaSamplerClient {
    private AtomicInteger counter = new AtomicInteger(0);
    private JSONObject template = new JSONObject();
    @Override
    public void setupTest(JavaSamplerContext context) {
        template.put("name", "HelloJsonSample");
        template.put("version", "1.0.0");
    }

    @Override
    public Arguments getDefaultParameters() {
        Arguments arguments = new Arguments();
        arguments.addArgument("url", "http://localhost:8081/post");
        arguments.addArgument("key1", "value1");
        return arguments;
    }

    @Override
    public void teardownTest(JavaSamplerContext context) {
        super.teardownTest(context);
    }

    public SampleResult runTest(JavaSamplerContext javaSamplerContext) {
        int count = counter.incrementAndGet();
        template.put("count", count);
        String val = javaSamplerContext.getParameter("key1");
        template.put("key1", val);
        // csv变量
        String person = javaSamplerContext.getJMeterVariables().get("person");
        String age = javaSamplerContext.getJMeterVariables().get("age");
        template.put("person", person);
        template.put("age", age);
        SampleResult sampleResult = new SampleResult();
        HttpResponse httpResponse = null;
        String body = template.toJSONString();
        String url = javaSamplerContext.getParameter("url");
        sampleResult.setSamplerData(body);
        try {
            sampleResult.sampleStart();
            httpResponse = HttpRequest.post(url).body(body).timeout(60000).execute();
            sampleResult.sampleEnd();
        } catch (Exception e) {
            e.printStackTrace();
            sampleResult.sampleEnd();
            sampleResult.setSuccessful(false);
            sampleResult.setResponseMessage(e.getMessage());
            return sampleResult;
        }
        String resBody = httpResponse.body();
        // 解析返回报文
        JSONObject resJson = JSON.parseObject(resBody);
        String code = resJson.getString("code");
        String retMsg = resJson.getString("retMsg");
        sampleResult.setResponseCode(code);
        sampleResult.setResponseMessage(retMsg);
        sampleResult.setResponseData(httpResponse.bodyBytes());
        sampleResult.setSentBytes(httpResponse.bodyBytes().length);
        if(!Objects.equals("000000", code)) {
            sampleResult.setSuccessful(false);
        } else {
            sampleResult.setSuccessful(true);
        }
        return sampleResult;
    }
}

a.在runTest方法里动态获取了参数person和age

关闭jmeter重新打包运行

一个线程跑两次:

可见读到了csv中的内容了。

扩展:分布式运行jmeter

有时候,jmeter客户端可能自身成为了一个瓶颈,比如在java请求中需要对报文做加解密等耗费CPU资源的操作,这时候可以考虑分布式运行jmeter。

官方说明:

Apache JMeter - Apache JMeter Distributed Testing Step-by-step

具体操作步骤:

为了演示方便,下面都是在本机windows上操作的,在linux上原理一样,只是把.bat后缀去掉。

1.把原jmeter复制三份

分别作为master(主控节点)、slave slave2(服务节点)

其中主控节点用于拉起压测,服务节点用于运行压测。

2.修改jmeter.properties

修改master的jmeter.properties配置,在最下方加上这几张

# 可以直接按ip和端口指定,或只指定host,端口默认就是1099了
remote_hosts=localhost:1199,localhost:1299
#server_port=1099
server.rmi.ssl.disable=true

特别关键的一步:server_port=1099这个要在master上注掉,因为我这里不想让他作为服务节点,而只是主控节点,不让他占用1099端口。

修改slave:

server_port=1199
server.rmi.ssl.disable=true
server.rmi.localport=0

指定了jmeter服务节点的端口是1199,他通过这个端口接收master发出的任务。

修改slave2:

server_port=1299
server.rmi.ssl.disable=true
server.rmi.localport=0

设置他的端口是1299。

这样就配置好了。

3.启动服务节点

双击运行slave和slave2下的命令:jmeter-server.bat 

25881、25916是他的localport,不用管。

4.启动master GUI界面并测试

运行master的jmeter.bat启动GUI界面:

以java hello world为例,运行压测(这个示例属于空跑,内部sleep1毫秒):

10线程运行10秒:

只运行1199这个服务节点试下:

这个是1199对应的服务控制台打印的日志,表示接收到了master的任务并运行了。

tps为619

再两个都启动试下:

TPS为1237,大概是619的两倍。

注意事项

1.为了测试方便,每次运行前清理上次运行结果,右侧那两个扫把样子的图标,一个是清理选中的测试结果,一个是清理所有的测试结果。一般清理所有的即可。

2.快速启用禁用组件,比如查看结果树,可能会占用点资源,那么在正式测试时,可以将其禁用。也可以鼠标右键选择enable disable。


原文地址:https://blog.csdn.net/brimsullowr/article/details/144339277

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!