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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 06:05:27