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

JMeter API中Sampler节点返回null,正则提取器添加失败求助

问题描述

尝试为所有Content-Type为text/html的样本添加正则提取器,但Sampler节点始终返回null。响应体正常且能返回正确结果,排查后推测是JMeter版本更新(当前为5.6.3)导致原有代码失效。相关代码如下,请提供修复建议:

package org.zkoss.testing.jmeter;

import org.apache.jmeter.config.Argument;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.exceptions.IllegalUserActionException;
import org.apache.jmeter.extractor.RegexExtractor;
import org.apache.jmeter.extractor.gui.RegexExtractorGui;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.tree.JMeterTreeModel;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
import org.apache.jmeter.protocol.http.proxy.ProxyControl;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.testelement.TestElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ZkProxyControl extends ProxyControl {
    private static final long serialVersionUID = 2851218983765186150L;

    private static String AU_PATH = "ZkProxyControlGui.auPath";

    private static final int DEFAULT_PORT = 9999;
    private static final String DEFAULT_AU_PATH = "/zkau";
    private static final String DEFAULT_EXCLUDED_PATTERN = ".*/zkau/web/.*";
    private static final Logger LOG = LoggerFactory.getLogger(ZkProxyControl.class);

    public ZkProxyControl() {
        super();
        this.setPort(DEFAULT_PORT);
        this.setAuPath(DEFAULT_AU_PATH);
        this.addExcludedPattern(DEFAULT_EXCLUDED_PATTERN);
    }

    public synchronized void deliverSampler(HTTPSamplerBase sampler,
                                            TestElement[] subConfigs, SampleResult result) {

        super.deliverSampler(sampler, subConfigs, result);

        final String auPath = getAuPath();

        if (result.getContentType().startsWith("text/html")) {// zul page

            JMeterTreeModel treeModel = GuiPackage.getInstance().getTreeModel();
            JMeterTreeNode newNode = treeModel.getNodeOf(sampler);
            LOG.warn("Sampler node: " + newNode);
            try {
                //add a RegexExtractor for retrieve the ZK dtid
                RegexExtractor extr = new RegexExtractor();
                extr.setProperty(TestElement.GUI_CLASS, RegexExtractorGui.class.getName());
                extr.setName("Regular Expression Extractor");
                extr.setRefName("dtid");
                extr.setRegex("dt:\'(.+?)\',cu");
                extr.setTemplate("$1$");
                extr.setDefaultValue("not found");
                treeModel.addComponent(extr, newNode);
            } catch (IllegalUserActionException e) {
                e.printStackTrace();
            }
        } else if (!auPath.isEmpty()) { // zkau request


            String path = sampler.getPath();
            int i = path.indexOf("jsessionid");
            if (i > -1)
                path = path.substring(0, i - 1);

            if (path.endsWith(auPath)) {
                Arguments args = sampler.getArguments();
                Argument arg = args.getArgument(0);
                if ("dtid".equals(arg.getName())) {//replace dtid value to a EL variable.
                    arg.setValue("${dtid}");
                }
            }
        }
    }

    public String getAuPath() {
        return this.getPropertyAsString(AU_PATH);
    }

    public void setAuPath(String auPath) {
        this.setProperty(AU_PATH, auPath, "");
    }
}
修复建议

核心问题分析

JMeter 5.x版本中,super.deliverSampler()执行后,采样器并未立刻完成树模型节点的注册,导致treeModel.getNodeOf(sampler)直接返回null。此外原代码缺少空值判断,容易触发空指针异常。

修复后的代码

package org.zkoss.testing.jmeter;

import org.apache.jmeter.config.Argument;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.exceptions.IllegalUserActionException;
import org.apache.jmeter.extractor.RegexExtractor;
import org.apache.jmeter.extractor.gui.RegexExtractorGui;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.tree.JMeterTreeModel;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
import org.apache.jmeter.protocol.http.proxy.ProxyControl;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.testelement.TestElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ZkProxyControl extends ProxyControl {
    private static final long serialVersionUID = 2851218983765186150L;

    private static String AU_PATH = "ZkProxyControlGui.auPath";

    private static final int DEFAULT_PORT = 9999;
    private static final String DEFAULT_AU_PATH = "/zkau";
    private static final String DEFAULT_EXCLUDED_PATTERN = ".*/zkau/web/.*";
    private static final Logger LOG = LoggerFactory.getLogger(ZkProxyControl.class);
    // 重试次数及间隔,确保采样器节点完成注册
    private static final int MAX_RETRY = 3;
    private static final int RETRY_INTERVAL = 100;

    public ZkProxyControl() {
        super();
        this.setPort(DEFAULT_PORT);
        this.setAuPath(DEFAULT_AU_PATH);
        this.addExcludedPattern(DEFAULT_EXCLUDED_PATTERN);
    }

    public synchronized void deliverSampler(HTTPSamplerBase sampler,
                                            TestElement[] subConfigs, SampleResult result) {

        super.deliverSampler(sampler, subConfigs, result);

        final String auPath = getAuPath();

        if (result.getContentType() != null && result.getContentType().startsWith("text/html")) {
            JMeterTreeModel treeModel = GuiPackage.getInstance().getTreeModel();
            JMeterTreeNode newNode = null;
            int retryCount = 0;

            // 重试获取节点,等待采样器完成树模型注册
            while (newNode == null && retryCount < MAX_RETRY) {
                newNode = treeModel.getNodeOf(sampler);
                if (newNode == null) {
                    try {
                        Thread.sleep(RETRY_INTERVAL);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        LOG.error("线程中断,终止节点获取", e);
                        break;
                    }
                    retryCount++;
                }
            }

            LOG.warn("Sampler node: " + newNode);
            if (newNode != null) {
                try {
                    RegexExtractor extr = new RegexExtractor();
                    extr.setProperty(TestElement.GUI_CLASS, RegexExtractorGui.class.getName());
                    extr.setName("Regular Expression Extractor");
                    extr.setRefName("dtid");
                    extr.setRegex("dt:\'(.+?)\',cu");
                    extr.setTemplate("$1$");
                    extr.setDefaultValue("not found");
                    treeModel.addComponent(extr, newNode);
                } catch (IllegalUserActionException e) {
                    LOG.error("添加正则提取器失败", e);
                }
            } else {
                LOG.error("重试{}次后仍无法找到采样器节点,无法添加正则提取器", MAX_RETRY);
            }
        } else if (auPath != null && !auPath.isEmpty()) {
            String path = sampler.getPath();
            if (path != null) {
                int i = path.indexOf("jsessionid");
                if (i > -1) {
                    path = path.substring(0, i - 1);
                }

                if (path.endsWith(auPath)) {
                    Arguments args = sampler.getArguments();
                    if (args != null && args.getArgumentCount() > 0) {
                        Argument arg = args.getArgument(0);
                        if ("dtid".equals(arg.getName())) {
                            arg.setValue("${dtid}");
                        }
                    }
                }
            }
        }
    }

    public String getAuPath() {
        return this.getPropertyAsString(AU_PATH);
    }

    public void setAuPath(String auPath) {
        this.setProperty(AU_PATH, auPath, "");
    }
}

关键优化点

  • 添加重试机制:等待采样器完成树模型节点注册,解决newNode为null的问题
  • 增加空值判断:避免result.getContentType()、sampler.getPath()等对象为空时触发异常
  • 优化日志输出:用LOG.error替代e.printStackTrace(),便于日志统一管理
  • 增强代码健壮性:对参数、请求路径等关键环节增加校验逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 11:10:05