如何在JMeter中还原应用日负载曲线的负载测试配置
在JMeter中无需外部工具实现随时间动态调整线程数(匹配负载曲线)
不用依赖插件或外部软件,仅用JMeter原生元件就能实现线程数随时间匹配负载曲线的需求,核心思路是通过全局存储负载数据+动态修改线程组线程数来实现,具体步骤如下:
1. 整理负载曲线数据
把你的每日负载变化曲线转换成「时间点-目标线程数」的结构化数据,保存为CSV文件(比如load_profile.csv),格式示例:
0,10 300,50 600,80 900,50 1800,10
- 第一列是测试启动后的累计时间(单位:秒)
- 第二列是对应时间点需要达到的线程数
- 确保时间点按从小到大排序
2. 加载负载数据到全局变量
添加一个Setup Thread Group(仅在测试开始时执行一次),用来读取CSV数据并存储到全局属性中:
- 在Setup Thread Group下添加
CSV Data Set Config:- 文件名:填写
load_profile.csv的路径 - 变量名:
time_point,target_threads - 共享模式:
All threads - 循环次数:
Forever
- 文件名:填写
- 在CSV元件下方添加
JS223 Sampler(选择Groovy语言),写入以下代码将数据存入全局映射:
def loadMap = props.get("loadMap") ?: new HashMap<>() loadMap.put(vars.get("time_point") as Integer, vars.get("target_threads") as Integer) props.put("loadMap", loadMap) // 当读取到CSV末尾时停止循环 if (vars.get("time_point") == null) { ctx.getThreadGroup().stop() }
3. 配置动态线程组
添加一个Regular Thread Group,基础配置:
- 线程数:
0(初始无运行线程) - Ramp-Up时间:
0 - 循环次数:
Forever(保持线程组持续运行,直到手动停止或负载曲线结束)
3.1 添加线程数调整逻辑
在Regular Thread Group下添加JS223 Timer(选择Groovy语言),设置延迟为1000毫秒(每秒检查一次,可根据精度调整),写入以下代码实现动态调整:
// 计算测试已运行时间(秒) def elapsed = (System.currentTimeMillis() - ctx.getThreadGroup().getStartTime()) / 1000 // 获取全局负载映射 def loadMap = props.get("loadMap") if (!loadMap) { log.error("未加载负载配置数据,无法调整线程数") return } // 找到当前时间对应的目标线程数(支持线性插值) def sortedTimes = loadMap.keySet().sort() def targetThreads = 0 for (int i = 0; i < sortedTimes.size(); i++) { def currentTimePoint = sortedTimes.get(i) if (elapsed <= currentTimePoint) { if (i == 0) { targetThreads = loadMap.get(currentTimePoint) } else { // 线性插值计算中间时间的线程数,让负载变化更平滑 def prevTimePoint = sortedTimes.get(i-1) def prevThreads = loadMap.get(prevTimePoint) def currThreads = loadMap.get(currentTimePoint) targetThreads = prevThreads + (currThreads - prevThreads) * (elapsed - prevTimePoint) / (currentTimePoint - prevTimePoint) targetThreads = targetThreads as Integer } break } // 超过最后一个时间点时,保持最后一个线程数 if (i == sortedTimes.size() - 1) { targetThreads = loadMap.get(currentTimePoint) } } // 获取当前线程组的实际线程数 def threadGroup = ctx.getThreadGroup() def currentThreads = threadGroup.getNumberOfThreads() // 动态调整线程数 if (targetThreads != currentThreads) { log.info("调整线程数:当前{} → 目标{}", currentThreads, targetThreads) threadGroup.setNumberOfThreads(targetThreads) threadGroup.testStarted() // 强制线程组应用新的线程数配置 }
3.2 添加空采样器保持线程运行
在Regular Thread Group下添加Test Action Sampler,设置Duration (ms)为1000,让线程每秒执行一次空操作,避免线程空转导致的资源浪费。
4. 验证与监控
添加Summary Report、Graph Results等监听器,实时查看线程数变化和请求指标,确认实际负载与目标曲线是否一致。
注意事项
- 若负载曲线是骤变(而非平滑变化),可删除代码中的线性插值逻辑,直接取当前时间点之前最近的目标线程数。
- Groovy在JMeter中的性能优于BeanShell,尽量选择Groovy作为JS223元件的语言。
- 如果测试时间超过负载曲线的最后一个时间点,线程数会保持最后一个值,若需要自动停止测试,可在Timer中添加判断逻辑,当elapsed超过最大时间点时调用
ctx.getThreadGroup().stop()。
内容的提问来源于stack exchange,提问作者Timofey Nikitin
相关产品推荐
相关产品推荐

