Abstracta JMeter Java DSL - 使用 RPSThreadGroup 优雅地完成线程

问题描述 投票:0回答:2

我想运行一个测试用例 x 时间,每秒请求 y 数量。我正在使用 RPSThreadGroup(每秒请求线程组)来实现此目的。

我面临的问题:当时间耗尽时,测试完全停止,最终的活动线程没有正常完成。这是因为一旦 RPS 计划完成,线程就会停止,如此处所述。这会导致以下异常:

[ThreadgroupNameHere-ThreadStarter 1-1] ERROR org.apache.jmeter.functions.Jexl2Function -- An error occurred while evaluating the expression "props.get('lambdaScript5').run(new('us.abstracta.jmeter.javadsl.core.util.PropertyScriptBuilder$PropertyScriptVars',ctx,log))"

org.apache.commons.jexl2.JexlException$Cancel: org.apache.jmeter.functions.Jexl2Function.execute@94![0,128]: 'props.get('lambdaScript5').run(new ('us.abstracta.jmeter.javadsl.core.util.PropertyScriptBuilder$PropertyScriptVars', ctx, log));' execution cancelled

Non HTTP response code: java.lang.IllegalArgumentException/Non HTTP response message: Host may not be blank

如何确保最终线程根本不执行或正常完成?

我的代码:

private static RpsThreadGroup getRpsThreadgroup(String threadGroupName, int maxThreads, int requestsPerSecond, int rampDurationInSeconds, int holdDurationInMinutes) {
    return rpsThreadGroup(threadGroupName)
        .maxThreads(maxThreads)
        .rampToAndHold(
            requestsPerSecond,
            Duration.ofSeconds(rampDurationInSeconds),
            Duration.ofMinutes(holdDurationInMinutes)
        )
        .rampTo(
            0,
            Duration.ofSeconds(5)
        );
}

注意:实际的 HttpSampler 在我的代码的不同阶段添加到线程组中。

getRpsThreadgroup()
纯粹是为了制作线程组并配置它。

假设我运行了一个测试 30 秒,每秒 1 个请求,执行了大约 30 个线程,但最后几个线程失败了,因为它们从未正确完成。请参阅image

我尝试将每秒请求数降至 0 并保持几秒钟。然而,这似乎不起作用。我得到了相同的结果(1+ 个失败的请求)。

java multithreading jmeter performance-testing
2个回答
0
投票

我无法使用以下 Java 代码片段重现您的问题:

TestPlanStats stats = testPlan(
        rpsThreadGroup()
                .maxThreads(50)
                .rampTo(20, Duration.ofSeconds(10))
                .rampTo(10, Duration.ofSeconds(10))
                .rampToAndHold(30, Duration.ofSeconds(5), Duration.ofSeconds(10))
                .children(
                        httpSampler("https://blazedemo.com")
                )
).run();
System.out.println("Total requests : " + stats.overall().samplesCount());
System.out.println("Failed requests: " + stats.overall().errorsCount());

查看您收到的错误:

主机不能为空

我可以假设您已经以某种方式参数化了 HTTP 请求采样器的“服务器名称或 IP”字段,并且当 JMeter 向线程发送关闭信号时,此参数化很可能会失败。

您可能希望将端点声明为 JMeter 属性,这样在运行测试的 JVM 运行之前它将可用。

今后考虑提供一个最小的可重现示例,这样我们就能够提出更全面的解决方案,而不必查看我们的水晶球。

此外,从 JMeter 5.6.x 开始,可以以编程方式创建测试计划,无需任何第 3 方 DSL。


0
投票

我在abstracta jmeter-java-dsl下打开了一个GitHub问题。 DSL 的主要维护者能够重现我的问题并声明如下:

原因是由于 rpsThreadGroup(使用 ConcurrencyThreadGroup + VariableThroughputTime)+ lambdas(使用 jexl 预处理器)。

当线程需要停止运行时,变量ThroughputTimer 会发送停止信号 到 ConcurrencyThreadGroup 中断线程,如果 jexl 正在中间/执行,然后抛出异常并结束 与您注意到的行为。它还可能会打断其他 事情,你可能会得到其他错误(例如:一些 IOException, 套接字关闭或类似的东西)。

作为临时解决方案,他提供了以下代码片段,替换默认的 RpsThreadGroup:

import kg.apc.jmeter.JMeterPluginsUtils;
import kg.apc.jmeter.timers.VariableThroughputTimer;
import kg.apc.jmeter.timers.VariableThroughputTimerGui;
import org.apache.jmeter.gui.util.PowerTableModel;
import org.apache.jmeter.sampler.TestAction;
import org.apache.jmeter.sampler.gui.TestActionGui;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.property.CollectionProperty;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jorphan.collections.HashTree;
import us.abstracta.jmeter.javadsl.core.BuildTreeContext;
import us.abstracta.jmeter.javadsl.core.threadgroups.RpsThreadGroup;

public class NonInterruptingRpsThreadGroup extends RpsThreadGroup {
    private static int timerId = 1;

    public NonInterruptingRpsThreadGroup(String name) {
        super(name);
    }

    @Override
    public HashTree buildTreeUnder(HashTree parent, BuildTreeContext context) {
        HashTree ret = parent.add(buildConfiguredTestElement());
        HashTree timerParent = counting == EventType.ITERATIONS ? ret.add(buildTestAction()) : ret;

        timerParent.add(buildTimer());
        children.forEach(c -> context.buildChild(c, ret));

        return ret;
    }

    private TestElement buildTestAction() {
        TestAction ret = new TestAction();

        ret.setAction(TestAction.PAUSE);
        ret.setDuration("0");
        configureTestElement(ret, "Flow Control Action", TestActionGui.class);

        return ret;
    }

    private TestElement buildTimer() {
        VariableThroughputTimer ret = new NonInterruptingVariableThroughputTimer();

        ret.setData(buildTimerSchedulesData());
        configureTestElement(ret, buildTimerName(timerId++), VariableThroughputTimerGui.class);

        return ret;
    }

    public static class NonInterruptingVariableThroughputTimer extends VariableThroughputTimer {
        @Override
        protected void stopTest() {
            // This is actually the main change from the original code of rpsThreadGroup.
            JMeterContextService.getContext().getThreadGroup().tellThreadsToStop();
        }

    }

    private String buildTimerName(int id) {
        return "rpsTimer" + id;
    }

    private CollectionProperty buildTimerSchedulesData() {
        PowerTableModel table = new PowerTableModel(
            new String[]{"Start RPS", "End RPS", "Duration, sec"},
            new Class[]{String.class, String.class, String.class}
        );

        schedules.forEach(s -> table.addRow(s.buildTableRow()));

        return JMeterPluginsUtils.tableModelRowsToCollectionProperty(table, "load_profile");
    }
}

您可以在此处详细阅读 GitHub 问题。

© www.soinside.com 2019 - 2024. All rights reserved.