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

Java-JMeter-DSL:如何让ForEachController内采样器按值分线程顺序执行

问题

我正在用Java编写JMeter-DSL脚本,当前实现逻辑如下:

  • 通过httpSampler调用endpoint1(GET)获取名称列表(如name1、name2等,内容取决于数据库数据)
  • 使用forEachController遍历每个名称,针对每个名称依次调用:
    1. endpoint2(GET,传入名称作为查询参数)
    2. endpoint3(PATCH,使用endpoint2返回的数据)
    3. endpoint4(POST,使用endpoint2返回的数据)

目前脚本运行正常,但我希望每个名称对应的这三个采样器能在独立线程中顺序执行。请问是否可以在forEachController内嵌套threadGroup?若可以该如何实现?或者有其他可行方案?

以下是我的脚本片段:

var result = testPlan(

        threadGroup(1, 1,

            httpHeaders()
                .header("header1", "value1"),

            httpCache()
                .disable(),

            httpSampler(endpoint1)
                .method(HTTPConstants.GET)
                .children(jsr223PreProcessor(s -> {
                    ..
                }))
                .children(jsr223PostProcessor(s -> {
                    // response processed here
                })),

            forEachController(VARIABLES, VARIABLE,

               // these samplers should run sequentially in separate thread for each value 
                httpHeaders()
                    .header("header1", "${"+VARIABLE+"}"),

                httpSampler(endpoint2) 
                    .method(HTTPConstants.GET)
                    .children(jsr223PreProcessor(s -> {
                        ..
                    }))
                    .children(jsr223PostProcessor(s -> {
                        // response processed here
                    })),

                httpSampler(endpoint3) 
                    .method(HTTPConstants.PATCH)
                    .body("body here")
                    .children(jsr223PreProcessor(s -> {
                        ..
                    }))
                    .children(jsr223PostProcessor(s -> {
                        // response processed here
                    })),

                httpSampler(endpoint4)
                    .method(HTTPConstants.POST)
                    .body("body here")
                    .children(jsr223PreProcessor(s -> {
                        ..
                    }))
                    .children(jsr223PostProcessor(s -> {
                        // response processed here
                    }))

            )

        ),

        jtlWriter(reportPath + "/success")
            .logOnly(JtlWriter.SampleStatus.SUCCESS),

        jtlWriter(reportPath + "/error")
            .logOnly(JtlWriter.SampleStatus.ERROR).withAllFields(),

        htmlReporter(reportPath + "/reports"),

        autoStop()
            .when(errors().total().greaterThan(0L)),

    responseFileSaver(reportPath + "/response/")

).run();
解决方案

首先明确:JMeter中线程组(Thread Group)不能嵌套在任何控制器(包括forEachController)内部,线程组是测试计划的顶级元素,只能直接放在testPlan下。你无法在forEachController里嵌套threadGroup,下面是两种可行的替代方案:

方案一:导出名称列表到文件,用独立线程组参数化执行

  1. 第一步:修改原有线程组,把名称列表写入CSV文件
    在endpoint1的JSR223后置处理器中,将解析后的名称列表写入CSV:

    jsr223PostProcessor(s -> {
        // 假设已解析出名称列表List<String> names
        List<String> names = ...; // 你的解析逻辑
        try (PrintWriter writer = new PrintWriter(new File("names.csv"))) {
            names.forEach(writer::println);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    })
    
  2. 第二步:新增独立线程组,读取CSV执行任务
    在testPlan下添加新线程组,线程数设为名称的数量,用csvDataSet读取CSV文件,然后顺序执行三个采样器:

    testPlan(
        // 原有获取名称并写入文件的线程组
        threadGroup(1, 1, ...),
        // 新的执行线程组
        threadGroup(names.size(), 1, // 线程数匹配名称数量,循环1次
            csvDataSet()
                .file("names.csv")
                .variableNames("CURRENT_NAME"),
            httpHeaders().header("header1", "${CURRENT_NAME}"),
            httpSampler(endpoint2)
                .method(HTTPConstants.GET)
                .queryParam("name", "${CURRENT_NAME}") // 传入名称参数
                .children(...), // 你的前置/后置处理器
            httpSampler(endpoint3)
                .method(HTTPConstants.PATCH)
                .body(...) // 使用endpoint2返回的数据
                .children(...),
            httpSampler(endpoint4)
                .method(HTTPConstants.POST)
                .body(...) // 使用endpoint2返回的数据
                .children(...)
        ),
        // 其余报告、监听器配置
        jtlWriter(...),
        htmlReporter(...)
    ).run();
    

    注意:要确保第一个线程组执行完再启动第二个,可以添加orderController控制顺序,或者给第二个线程组设置启动延迟。

方案二:用Parallel Controller实现伪并行处理

如果不想生成中间文件,可以用JMeter的Parallel Controller(需先引入依赖),在forEachController内给每个名称的任务包裹一个并行分支,分支内保持三个采样器的顺序:

  1. 引入Parallel Controller依赖
    Maven项目添加依赖:

    <dependency>
        <groupId>kg.apc</groupId>
        <artifactId>jmeter-plugins-parallel</artifactId>
        <version>0.10</version>
    </dependency>
    
  2. 修改脚本结构
    将forEachController内的采样器放入parallelController的分支:

    forEachController(VARIABLES, VARIABLE,
        parallelController(
            // 每个分支处理一个名称的三个步骤,内部顺序执行
            threadGroup(1, 1,
                httpHeaders().header("header1", "${"+VARIABLE+"}"),
                httpSampler(endpoint2).method(HTTPConstants.GET).children(...),
                httpSampler(endpoint3).method(HTTPConstants.PATCH).children(...),
                httpSampler(endpoint4).method(HTTPConstants.POST).children(...)
            )
        )
    )
    

    注意:Parallel Controller是在同一个线程组内模拟并行,并非真正的独立线程,但能满足每个名称的三个步骤并行处理且内部顺序执行的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 03:22:06