You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JMeter WebSocket测试如何跨线程组复用已有连接并控制执行顺序

JMeter WebSocket 跨线程组执行顺序控制与连接复用方案

核心问题根因

  • WebSocket连接无法跨线程组复用:JMeter WebSocket Samplers 建立的连接绑定在所属线程的上下文变量中,不同线程组属于独立线程上下文,若依赖测试计划的「连续运行线程组」选项,上一个线程组执行完后线程会直接销毁,持有的WebSocket连接也会同步释放,后续线程组自然无法访问已建立的连接。
  • 执行顺序错乱:默认配置下多个线程组并行启动,没有同步阻塞机制时,请求执行顺序完全受CPU调度影响,会出现Participant端先执行、读取到上一轮测试遗留旧sessionId的问题。

可直接落地的配置步骤

基础配置修正

  • 不要单独创建第三个线程组存放send请求:send是Presenter端基于自有WebSocket连接的写操作,直接归属到Presenter线程组即可,从根源上避免跨线程复用连接的需求。
  • 取消勾选测试计划的run thread groups consecutively(连续运行线程组)选项,两个线程组保持并行启动模式,线程数均设为1,循环次数按实际测试轮次配置。
  • 测试计划根节点新增JSR223 Sampler,测试启动时先清空历史缓存的sessionId,避免旧值干扰,代码如下:
    props.remove("sessionId")
    props.remove("sessionReadyLatch")
    props.remove("participantReadyLatch")
    props.remove("sendCompleteLatch")
    

基于CountDownLatch实现严格顺序控制

用JDK自带的CountDownLatch做线程间同步阻塞,不需要额外安装插件,即可严格按指定流程执行:

  • Presenter线程组第一阶段:Login -> Connect -> 创建session的Subscribe请求
    保留原有sessionId解析逻辑,在创建session请求的JSR223 PostProcessor中追加同步逻辑,通知Participant端session已生成:
    import groovy.json.JsonSlurper;
    import java.util.concurrent.CountDownLatch;
    
    def response = prev.getResponseDataAsString();
    String[] res = response.split();
    def json = new JsonSlurper().parseText(res[res.size() -1]);
    
    vars.put("sessionUUID", json.data.sessionUUID);
    props.put("sessionId", vars.get("sessionUUID"));
    
    // 触发session就绪通知
    CountDownLatch sessionReadyLatch = new CountDownLatch(1);
    props.put("sessionReadyLatch", sessionReadyLatch);
    sessionReadyLatch.countDown();
    
  • Participant线程组前置等待
    在Connect请求之后、Subscribe请求之前新增JSR223 Sampler,阻塞等待Presenter生成新sessionId后再执行订阅,避免拿旧值:
    import java.util.concurrent.CountDownLatch;
    
    CountDownLatch sessionReadyLatch = props.get("sessionReadyLatch");
    // 最长等待10秒,超时直接抛错终止流程
    if (sessionReadyLatch != null) {
        boolean awaitResult = sessionReadyLatch.await(10, java.util.concurrent.TimeUnit.SECONDS);
        if (!awaitResult) {
            prev.setSuccessful(false);
            prev.setResponseMessage("Wait sessionId generate timeout");
            return;
        }
    }
    // 读取本轮新生成的sessionId存入当前线程变量
    String currentSessionId = props.get("sessionId");
    if (currentSessionId == null || currentSessionId.trim().isEmpty()) {
        prev.setSuccessful(false);
        prev.setResponseMessage("Got empty sessionId");
        return;
    }
    vars.put("sessionUUID", currentSessionId);
    
  • Participant线程组第一阶段:Connect -> Subscribe
    订阅流程执行完成后,在Subscribe请求后新增JSR223 PostProcessor,通知Presenter端自己已完成订阅准备:
    import java.util.concurrent.CountDownLatch;
    
    CountDownLatch participantReadyLatch = new CountDownLatch(1);
    props.put("participantReadyLatch", participantReadyLatch);
    participantReadyLatch.countDown();
    
  • Presenter线程组第二阶段等待
    在创建session请求之后、send请求之前新增JSR223 Sampler,阻塞等待Participant完成订阅后再发消息:
    import java.util.concurrent.CountDownLatch;
    
    CountDownLatch participantReadyLatch = props.get("participantReadyLatch");
    if (participantReadyLatch != null) {
        boolean awaitResult = participantReadyLatch.await(10, java.util.concurrent.TimeUnit.SECONDS);
        if (!awaitResult) {
            prev.setSuccessful(false);
            prev.setResponseMessage("Wait participant subscribe timeout");
            return;
        }
    }
    
  • Presenter线程组执行send请求
    等待完成后直接执行WebSocket Single Write Sampler发送消息,因为连接是当前线程创建的,可以直接正常复用,不存在连接找不到的问题。发送完成后新增JSR223 PostProcessor,通知Participant端消息已发出,可以开始时延统计:
    import java.util.concurrent.CountDownLatch;
    
    CountDownLatch sendCompleteLatch = new CountDownLatch(1);
    props.put("sendCompleteLatch", sendCompleteLatch);
    sendCompleteLatch.countDown();
    
  • Participant线程组启动时延测试
    在Subscribe请求之后、包含WebSocket Single Read Sampler的循环控制器之前新增JSR223 Sampler,等待消息发送完成的通知,收到通知后再启动循环读消息、计算时延:
    import java.util.concurrent.CountDownLatch;
    
    CountDownLatch sendCompleteLatch = props.get("sendCompleteLatch");
    if (sendCompleteLatch != null) {
        boolean awaitResult = sendCompleteLatch.await(10, java.util.concurrent.TimeUnit.SECONDS);
        if (!awaitResult) {
            prev.setSuccessful(false);
            prev.setResponseMessage("Wait presenter send message timeout");
            return;
        }
    }
    
  • 每轮测试流程结束后,两个线程组都主动调用WebSocket Close Sampler断开当前连接,清空props中存的latch对象,避免下一轮测试受干扰。

注意事项

  • 不要尝试把WebSocket连接对象存入props跨线程传递:连接对象绑定了创建它的线程上下文,跨线程调用会出现IO状态异常、连接意外断开的问题,遵循哪个线程创建连接,哪个线程负责该连接的读写原则即可完全规避这类问题。
  • 所有等待逻辑都必须设置超时时间,不要无限阻塞,避免测试流程卡死。
  • 多轮循环测试时,每轮结束后必须清空上一轮生成的latch和sessionId缓存,避免状态污染。

内容的提问来源于stack exchange,提问作者Zhang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 05:51:15