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
相关产品推荐
相关产品推荐

