Java-JMeter-DSL:如何让ForEachController内采样器按值分线程顺序执行
我正在用Java编写JMeter-DSL脚本,当前实现逻辑如下:
- 通过
httpSampler调用endpoint1(GET)获取名称列表(如name1、name2等,内容取决于数据库数据) - 使用
forEachController遍历每个名称,针对每个名称依次调用:- endpoint2(GET,传入名称作为查询参数)
- endpoint3(PATCH,使用endpoint2返回的数据)
- 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,下面是两种可行的替代方案:
方案一:导出名称列表到文件,用独立线程组参数化执行
第一步:修改原有线程组,把名称列表写入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(); } })第二步:新增独立线程组,读取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内给每个名称的任务包裹一个并行分支,分支内保持三个采样器的顺序:
引入Parallel Controller依赖
Maven项目添加依赖:<dependency> <groupId>kg.apc</groupId> <artifactId>jmeter-plugins-parallel</artifactId> <version>0.10</version> </dependency>修改脚本结构
将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

