JMeter的Java请求取样器(Java Request Sampler)是性能测试中的一把万能钥匙,当需要的协议、逻辑无法被标准取样器覆盖时,就可以通过它编写定制化代码。
一,第一个 Java 请求
什么时候需要它?
测试自定义 TCP/UDP协议、RPC框架(如 Dubbo、gRPC)、消息队列(Kafka、RabbitMQ);需要复杂加密/解密、签名算法;调用硬件 SDK 或内部库;任何HTTP、JDBC等标准取样器搞不定的场景。
开发环境准备:
JDK 8+(与运行 JMeter 的版本一致);Maven或Gradle(推荐,管理依赖);JMeter 安装目录,依赖在 lib/ext 和 lib 下。
Maven 依赖(最小化):
<dependencies>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_core</artifactId>
<version>5.6.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_java</artifactId>
<version>5.6.3</version>
<scope>provided</scope>
</dependency>
</dependencies>
scope=provided 表示打包时不带走 JMeter 已提供的 jar,避免冲突。
实现接口:
推荐继承 AbstractJavaSamplerClient,需要以下四个方法:
getDefaultParameters():定义在 JMeter UI 中显示的参数,加载类时调用一次。
setupTest(JavaSamplerContext ctx):初始化资源(每个线程独立),每个线程启动前调用一次。
runTest(JavaSamplerContext ctx):执行核心测试逻辑,每次迭代都调用,必须返回 SampleResult。
teardownTest(JavaSamplerContext ctx):清理资源,每个线程结束后调用一次。
一个最简单的例子(生成随机数字作为响应):
代码示例:
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.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class RandomNumberSampler extends AbstractJavaSamplerClient {
private static final Logger log = LogManager.getLogger(RandomNumberSampler.class);
@Override
public Arguments getDefaultParameters() {
Arguments args = new Arguments();
args.addArgument("min", "1");
args.addArgument("max", "100");
return args;
}
@Override
public void setupTest(JavaSamplerContext context) {
log.info("Thread setup: min={}, max={}",
context.getParameter("min"), context.getParameter("max"));
}
@Override
public SampleResult runTest(JavaSamplerContext context) {
SampleResult result = new SampleResult();
result.sampleStart();
try {
int min = context.getIntParameter("min");
int max = context.getIntParameter("max");
int randomNum = min + (int)(Math.random() * (max - min + 1));
result.setResponseData("Random number: " + randomNum, "UTF-8");
result.setResponseCode("200");
result.setResponseMessage("Success");
result.setSuccessful(true);
} catch (Exception e) {
result.setResponseCode("500");
result.setResponseMessage(e.toString());
result.setSuccessful(false);
} finally {
result.sampleEnd();
}
return result;
}
@Override
public void teardownTest(JavaSamplerContext context) {
log.info("Thread teardown.");
}
}
部署与运行:
用 Maven 打包:mvn clean package,得到 your-jar.jar;将 jar 复制到 JMETER_HOME/lib/ext/;重启 JMeter;在测试计划中添加线程组 → 取样器 → Java 请求,在“类名称”下拉框中选择 RandomNumberSampler;下方会显示定义的参数 min、max,可直接修改;运行测试,查看结果树即可看到响应数据。
二、进阶深度整合复杂场景
与 JMeter 变量交互
通过 JavaSamplerContext 可以拿到 JMeterVariables,实现数据传递:
String userName = context.getJMeterVariables().get("username");
context.getJMeterVariables().put("token", calculateToken(userName));
还可以获取更丰富的上下文:
JMeterContext jmctx = context.getJMeterContext();
jmctx.getPreviousResult();
jmctx.getCurrentSampler();
jmctx.getThreadNum();
精细化控制 SampleResult
代码示例:
SampleResult result = new SampleResult();
result.setSampleLabel("我的Java请求");
result.sampleStart();
// 模拟子步骤
SampleResult sub = new SampleResult();
sub.sampleStart();
// ... 执行子操作
sub.sampleEnd();
sub.setSuccessful(true);
result.addSubResult(sub);
result.setResponseData(data, "UTF-8");
result.setDataType(SampleResult.TEXT); // 或 SampleResult.BINARY
result.setResponseHeaders("..."); // 若模拟HTTP,可设置头
result.setLatency(latency);
result.setSuccessful(true);
result.sampleEnd();
必须配对调用 sampleStart() 和 sampleEnd()。
多线程安全处理
若不勾选线程间共享,每个线程独立实例,setupTest/teardownTest 按线程独立执行,成员变量可安全存储线程私有资源。
若必须共享实例,需自行处理同步,或用 ThreadLocal 管理资源:
private ThreadLocal<MyClient> clientHolder = ThreadLocal.withInitial(MyClient::new);
@Override
public SampleResult runTest(JavaSamplerContext context) {
MyClient client = clientHolder.get();
// ...
}
使用 JMeter 属性与日志
import org.apache.jmeter.util.JMeterUtils;
String timeout = JMeterUtils.getPropDefault("my.timeout", "3000");
log.info("全局超时配置: {}", timeout);
日志输出到 jmeter.log,级别在 log4j2.xml 中调整。
实现复杂协议示例:一个简单的加密签名请求
代码示例:
public SampleResult runTest(JavaSamplerContext ctx) {
SampleResult sr = new SampleResult();
sr.sampleStart();
try (Socket socket = new Socket("127.0.0.1", 9000)) {
String body = ctx.getParameter("body");
String sign = md5(body + "secretKey");
String request = body + "|sign=" + sign;
// 发送与接收...
String response = readFromSocket(socket);
sr.setResponseData(response, "UTF-8");
sr.setSuccessful(true);
} catch (Exception e) {
sr.setSuccessful(false);
sr.setResponseMessage(e.getMessage());
} finally {
sr.sampleEnd();
}
return sr;
}
打断测试:实现 Interruptible 接口
若有阻塞操作,实现 Interruptible,以便 JMeter 停止时能立即中断:
public class MySampler extends AbstractJavaSamplerClient implements Interruptible {
private volatile boolean interrupted = false;
@Override
public boolean interrupt() {
interrupted = true;
return true;
}
@Override
public SampleResult runTest(JavaSamplerContext ctx) {
SampleResult result = new SampleResult();
result.sampleStart();
while (!interrupted) {
// 等待消息...
}
// ...
}
}
调试技巧
IDE 远程调试:启动 JMeter 加参数 -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005,然后在 IDE Attach 到 5005 端口。
增加日志:多输出 log.debug/error 关键中间值。
查看 jmeter.log 或控制台启动 JMeter 观察实时输出。
Maven 打包最佳实践
使用 maven-shade-plugin 合并第三方依赖,但排除 JMeter 已有的依赖:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.5.0</version> <executions> <execution> <phase>package</phase> <goals><goal>shade</goal></goals> <configuration> <artifactSet> <excludes> <exclude>org.apache.jmeter:*</exclude> <exclude>org.apache.commons:*</exclude> </excludes> </artifactSet> </configuration> </execution> </executions>
</plugin>
简单做法:直接丢编译后的 class 或 jar 到 lib/ext,配合远程调试验证。
三、Java请求对比JSR223取样器
性能:Java 请求是编译执行,极快;JSR223 (Groovy) 预编译后也很快,但略低于原生 Java。
依赖管理:Java 请求需将依赖打包为 jar 放入 lib/ext;JSR223 可动态加载脚本,但复杂逻辑难以维护。
可复用性:Java 请求可打包为公共 jar,强复用;JSR223 脚本容易复制粘贴,易失控。
适用场景:Java 请求适合固定、复杂、性能敏感的协议;JSR223 适合快速原型、一次性脚本和简单逻辑。
四、总结
继承AbstractJavaSamplerClient,实现runTest,掌握getDefaultParameters和SampleResult的使用。
与 JMeterVariables 交互,处理多线程安全,精细化控制结果,实现Interruptible支持停止。
将自定义Java请求作为JMeter的万能扩展,配合命令行和CI/CD,构建性能测试平台。