这段时间工作中本人对项目进行了一次压测,用到了不少常见的压测技术,和一些典型的场景,这里简单分享下。看完你可以解答如下一些问题:
- JMeter 如何发送有签名验签或者是加密的复杂请求?
- JMeter 如何控制线程步进式增长?
- JMeter 如何监控服务器负载?
- 如何监控被压测 JVM 状态?线程状态?
JMeter
- 官网地址:jmeter.apache.org/index.html
- 本地 JMeter 版本:5.1.1,下载地址
JMeter 添加 Java Request Sampler
绝大多数后端接口形式的请求报文都不是明文,大多都需要进行签名验签,我们项目也不例外。这就是遇到的第一个问题。好在 JMeter 也提供了自定义扩展功能,Java Request,用来 Java 代码自定义发送压测请求的功能。
首先要做的是创建一个单独的项目,目的是 签名验签并发送 HTTP 请求,想要与 JMeter 相结合还必须实现 org.apache.jmeter.protocol.java.sampler.JavaSamplerClient
接口或者继承 org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient
,创建好以后假如在 JMeter 界面上配置了当前创建类,JMeter 就会反过来调用你写的方法(钩子、SPI、插件)。
这两个类是在 JMeter 项目有引入,但是自己项目想要访问,可以用 Maven 引入。如果自己创建的项目不是 Maven,可以在 JMeter_HOME/lib/ext
文件夹下找到。所以在这里 Maven 的依赖范围我们选择到了 <scope>provided</scope>
。
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_java</artifactId>
<version>5.3</version>
<scope>provided</scope>
</dependency>
代码示例如下:
public class TestJavaRequestSamplerClient implements JavaSamplerClient {
// 做一些资源的初始化工作
public void setupTest(JavaSamplerContext context) {}
// 需要的默认参数,会展示在界面上
public Arguments getDefaultParameters() {
Arguments args = new Arguments();
args.addArgument("url", "");
args.addArgument("type", "");
return args;
}
// 需要执行的业务方法
public SampleResult runTest(JavaSamplerContext context) {
// 用于记录执行结果的状态
SampleResult sampleResult = new SampleResult();
// 获取 JMeter 中输入的用户参数
String url = context.getParameter("url", "默认值");
String type = context.getParameter("type", "默认值");
// 标记开始
sampleResult.sampleStart();
boolean flag = this.doRequest();
sampleResult.setSuccessful(flag);
// 标记暂停
// sampleResult.samplePause();
// 标记重启
// sampleResult.sampleResume();
// 标记结束
sampleResult.sampleEnd();
return sampleResult;
}
private boolean doRequest() {
// 组装报文
// 进行签名
// 发送 HTTP 请求
// 解析响应
return false;
}
//
public void teardownTest(JavaSamplerContext context) {}
}
接下来需要把这个项目打成 .jar
形式并拷贝到 {JMeter_HOME}\lib\ext
下。接着在 JMeter 界面上添加 Java Request 类型的 采样器 如下,下拉列表中看到 TestJavaRequestSamplerClient
类了
这里需要重点注意!添加的是 采样器下面的 Java 请求 。
是 Sampler > Java Request 而不是 Config Element > Java Request Defaults
这两个搞错会导致发不出去请求
特别注意几点:
- Maven 项目的编译等级不要选太高,建议 1.5
- 如果创建的项目还有除
ApacheJMeter_java
以外的依赖,也必须一同拷贝到{JMeter_HOME}\lib\ext
下- jar 文件放置好以后,重启 JMeter 才能识别
JMeter 添加一些常用插件
安装插件省略
- Stepping Thread Group(步进线程组)
这里已经被标记为弃用,新的名字为 Concurrency Thread Group
,不过用法也是大同小异。这个插件能很灵活配置线程的启动方式,目的是维护并发级别。
下图是一个本人使用实际压测配置
- Aggregate Report(监听器,聚合报告)
必须加上,统计 TPS 用
- Label:每个 JMeter 的 element(例如 HTTP Request)都有一个 Name 属性,这里显示的就是 Name 属性的值
- #Samples:此次测试中共发出了多少请求
- Average:平均响应时间——默认情况下是单个 Request 的平均响应时间
- Median:中位数,也就是 50% 用户的响应时间
- 90% Line:90% 用户的响应时间
- 95% Line:95% 用户的响应时间
- 99% Line:99% 用户的响应时间
- Min:最小响应时间
- Max:最大响应时间
- Error%:本次测试中出现错误的请求的数量/请求的总数
- Throughput:吞吐量——默认情况下表示每秒完成的请求数(Request per Second),可以认为是 TPS
- KB/Sec:每秒从服务器端接收到的数据量
- Transactions per Second(每秒事务处理量)
- Response Times Over Time(响应时间变化趋势图)
- PerfMon Metrics Collector(性能指标收集器)
假如要收集被压测服务器资源使用信息,可以用这个插件。首先需要在被压测服务器上启动 ServerAgent
程序,然后在 JMeter 界面配置目标机器以及监控指标,官方文档
下面是一个监控结果
!注意!在 Non-Gui 模式下,使用该插件时必须单独配置 FileName 属性,否则用 -l 参数生成的结果文件导入无法显示监控指标曲线 资料:stackoverflow.com/questions/5…
使用 NON GUI mode 执行脚本
由于 JMeter 界面运行时会损耗一部分的性能,会对压测结果造成偏差。所以一般在压测中使用 non-gui 模式 来运行 JMeter 脚本
在 JMeter 界面上配置好测试计划后,导出为 .jmx 文件,接着使用命令运行它(假如是在另一台 linux 服务器,首先保证需要有 JMeter 程序)
# ./jmeter -n -t demo.jmx -l 结果文件.jtl
上文我们配置一个了 TestJavaRequestSamplerClient
的 Java Request 要求两个参数 url 和 type,在 JMeter non-gui 模式下使用 -J 来传递
# ./jmeter -n -t demo.jmx -Jurl='http://127.0.0.1:8080/test' -Jtype='1' -l 结果文件.jtl
!非常重要!得到 xxx.jtl 结果文件之后,可以使用 JMeter 界面上的 FileName 文件选择框中导入来查看。假如导入时,界面上还显示着上一个报告的内容,!千万要清空后,再导入,否则会发生数据错乱!
JPorfiler
配置好上文列出的 JMeter 插件和配置以后,我们已经可以拿到应用的 TPS 和所对应的服务器资源情况。但如果此时发现程序性能有问题,我们还少个观察、监控应用的功能,来分析性能瓶颈,这里我们选择了 JProfiler
- 官网地址:www.ej-technologies.com/products/jp…
- 本地 JPorfiler 版本:10_1_5,下载地址
方式一【简单、推荐】:使用 Attach 模式(jpenable)
在远端服务器下载好对应的 linux 版本 jprofiler(jprofiler_linux_10_1_5.tar.gz),并解压,接着
然后在本地 Jprofiler 连接即可
方拾二【较麻烦】使用配置向导(添加 jvm 启动参数模式)
选择对应 tomcat 版本(SpringBoot 应用后面会讲)
选择远程
选择立即启动,不等待界面连接
填写远程机器 ip 地址
填写远程服务器 jprofiler 地址
这一步很关键,选择的是本地的 同版本 的 tomcat 启动脚本(startup.sh)。jprofiler
指定通信端口,默认即可
到这里为止,JProfiler 修改了两个文件,需要我们手动拷贝到远程服务器上
- C:\Users\xxxx.jprofiler10\config.xml:这个文件 JProfile 建议拷贝到远程服务器,但是实测不需要拷贝也能连上去
- startup_jprofiler.sh:这个文件必须要拷贝到远程服务器。与前文选择本地 tomcat 启动文件在一个路径,是 JProfiler 基于 startup.sh 修改一个启动脚本,对比可发现最后增加了几行。如下。这个脚本也需要拷贝到远程服务器。放在待压测应用的 tomcat bin 目录下。
接着执行启动脚本,用 JProfiler 连上即可
# sh ./startup_jprofiler.sh
常见操作
JProfiler 功能非常强大,这里只是当时用到的一些简单操作,后续用到再做补充
从概览中很清楚的能看到各种监控指标,同时可以随时保存当前监控快照,供后续回顾分析
用到的最多的就是
这是一个真实的压测情况快照
很容易就看到线程大片的红色,都被 Blocked,再看下当时线程的 dump,发现都是卡在日志写入
阻塞的原因很多:1. 磁盘读写慢;2. 其他的 IO 读写影响到了文件读写。这里不展开分析。