JMeter非GUI模式执行时线程阻塞,无法达成目标TPS求助
解决JMeter线程阻塞无法达到目标TPS的问题
核心问题定位
你的场景中200个线程里195个处于阻塞状态,结合动态请求头生成的JSR223脚本逻辑,大概率是脚本写法不当引发的线程竞争、非线程安全的哈希计算操作、共享资源锁冲突导致的性能瓶颈,而非硬件资源不足。
具体解决方案
1. 优化JSR223脚本,启用编译缓存
- 给所有JSR223 Pre/Post Processor勾选「Cache compiled script if available」选项,避免每次请求都重新编译Groovy脚本,减少CPU开销和因编译引发的锁竞争。
- 绝对不要在脚本中使用
synchronized同步块或共享可变对象(比如全局变量存储nonce、apikey),每个线程独立生成所需参数。
2. 替换非线程安全的哈希计算实现
MessageDigest(MD5)和Mac(HmacSHA512)都是非线程安全类,重复创建实例或跨线程共用会引发严重的线程竞争。改用ThreadLocal为每个线程维护独立实例:
// 初始化ThreadLocal存储哈希实例,每个线程独享 private static final ThreadLocal<MessageDigest> md5Digest = ThreadLocal.withInitial { MessageDigest.getInstance("MD5") } private static final ThreadLocal<Mac> hmacSha512 = ThreadLocal.withInitial { Mac.getInstance("HmacSHA512") } // 计算contentMD5 def requestBody = sampler.getArguments().getArgument(0).getValue() def md5Bytes = md5Digest.get().digest(requestBody.getBytes("UTF-8")) def contentMD5 = Base64.getEncoder().encodeToString(md5Bytes) md5Digest.get().reset() // 重置实例,避免下一次计算受残留数据影响 // 计算apikey示例 def hmac = hmacSha512.get() hmac.init(new SecretKeySpec("你的密钥".getBytes(), "HmacSHA512")) def apikeyBytes = hmac.doFinal("拼接字符串".getBytes("UTF-8")) def apikey = apikeyBytes.encodeHex().toString() hmac.reset()
3. 避免操作共享HeaderManager引发锁冲突
- 添加请求头时,直接操作当前采样器的专属HeaderManager,不要使用全局HeaderManager:
def headerManager = sampler.getHeaderManager() headerManager.add(new Header("contentMD5", contentMD5)) headerManager.add(new Header("client", "clientTypeAPP")) headerManager.add(new Header("nonce", System.currentTimeMillis().toString())) headerManager.add(new Header("apikey", apikey)) - 移除Post Processor的请求头删除逻辑:每个采样器的HeaderManager是线程独立的,动态添加的请求头不会传递给后续采样器,Post Processor的删除操作完全多余,反而会增加线程对HeaderManager的锁竞争。
4. 调整JMeter线程模型与JVM参数
- 重新计算所需线程数:线程数 = 目标TPS × 平均响应时间(秒)。比如目标100 RPS,响应时间1秒,100个线程足够,200个线程会徒增上下文切换开销。
- Non-GUI模式下添加优化的JVM启动参数(根据虚拟机内存调整Xms/Xmx):
JVM_ARGS="-Xms4g -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=100" jmeter -n -t test.jmx -l result.jtl - 修改
jmeter.properties减少IO开销:jmeter.save.saveservice.output_format=csv jmeter.save.saveservice.response_data=false jmeter.save.saveservice.samplerData=false
5. 精准定位阻塞锁来源
从线程转储中查看阻塞线程等待的具体锁对象:
- 如果是
java.lang.ClassLoader锁:启用JSR223脚本缓存即可解决。 - 如果是
org.apache.jmeter.protocol.http.control.HeaderManager锁:检查是否操作了全局HeaderManager,改用采样器专属实例。 - 如果是自定义类锁:排查脚本中是否有不必要的同步逻辑。
内容的提问来源于stack exchange,提问作者SAIR
相关产品推荐
相关产品推荐

