news 2026/9/29 14:07:43

JMeter低频压测实战:如何稳定实现每秒1次请求

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JMeter低频压测实战:如何稳定实现每秒1次请求

先说个有意思的事。很多人一听到“压测”两个字,脑子里浮现的都是几百上千并发、TPS 好几千的宏大场面。但实际工作中,我接到过不少需求恰恰相反——客户要求“每秒就发 1 次请求”,连续跑几个小时甚至几天。你别说,这种低频压测还真不是没事找事。

我第一次接到这个需求时也有点懵:1 秒 1 次,这还用压测?直接手工在浏览器里点不就完了?后来真做起来才发现,里面门道不少。为什么有人要这么干,用什么方式实现最稳,怎么确认真的做到了每秒 1 次,这些都是有讲究的。这篇文章就把我实际踩过的坑、验证过的方案完整写出来,给同样遇到这类需求的朋友一个参考。

先说这个任务是什么:用 JMeter 构造一个稳定的、每秒恰好发出 1 次 HTTP 请求的测试场景,并在此基础上做断言、看响应、出报告。它解决的问题是:模拟真实系统里低频但持续性的调用行为,验证服务在长时间低频请求下是否稳定,接口有没有内存泄漏、连接未释放、慢请求堆积这类“慢性病”。适合谁看?刚入门 JMeter 想搞明白定时器用法的测试新手,以及遇到过“低频压测”需求但不知道怎么做最靠谱的同行。

1. 先把场景说清楚:每秒1次到底测的是什么

1.1 高频压测做多了,低频场景反而容易忽略

做性能测试的人,习惯性地会把注意力放在“能不能扛住高并发”上。线程数往大了调,循环次数往多了拉,看 TPS 曲线像过山车一样冲上去,这才觉得像在做压测。

但真实业务不全是高并发。很多系统的请求特征恰恰是“低频、长尾、持续”。比如物联网设备上报:一台设备几分钟上报一次,但设备数量可能有几万台,单看每一台都是低频,整体聚合起来才是压力。再比如定时任务触发的回调、客户端的心跳检测、第三方平台的轮询——这些都是典型的低频请求场景。

用高频压测去测这类接口,得出的结论往往失真。因为高频请求会放大 CPU 竞争、线程池排队这些因素,掩盖了接口在稳定节奏下真正的问题。反过来,如果只测低频,很多人又觉得“这有什么好测的”,于是干脆不测,结果线上出了问题才发现是长期低频请求导致的连接泄漏或者内存缓慢增长。

1.2 哪些业务场景需要“每秒1次”

我整理了一下实际遇到过的需求,大致有这么几类:

  • 心跳/健康检查接口:客户端或注册中心每隔固定时间探活,比如 Nacos 客户端心跳。这种接口不需要高并发,但必须保证长时间稳定响应。
  • 定时拉取/轮询接口:比如前端轮询任务状态、后端定时拉取上游数据。频率通常不高,但链路长、依赖多,任何一个环节慢一点都会积累。
  • 消息推送后的回执确认:推送服务发送后等待接收方回执,频率由业务量决定,一般不高。
  • 网关或防火墙的限流规则验证:要验证“每秒只允许 1 个请求”的限流配置是否真的生效,就得用精确的每秒 1 次去触发。
  • 稳定性长跑测试:用固定低频持续跑 24 小时甚至 72 小时,观察内存、句柄、连接池的变化。这是低频压测最有价值的地方。

注意最后一条。很多团队做稳定性测试时,喜欢让脚本跑得尽量快,恨不得把机器打满。但真正的长稳测试反而不需要高并发——你要的是“持续、稳定、有规律的请求”,这样才能观察出系统有没有资源缓慢泄漏的问题。低频长跑比高频长跑更容易暴露这类慢性问题,因为高频时资源快速被占满,问题很快就暴露了,反而不符合真实场景的渐进式恶化。

1.3 1秒1次不算压测?这个误解该纠正了

有人会杠:每秒 1 次不叫压测,叫“探活”。这其实是对压测的窄化理解。

压测的本质是“用可控的请求压力,验证系统在指定条件下是否满足预期”。压力是相对的——对一个只处理低频轮询的轻量服务来说,每秒 1 次就是它的正常业务压力,通过拉高频率去“压”它反而没有意义。换个角度看,压测的粒度可以是频率,也可以是总量。每秒 1 次、连续跑 86400 秒,就是 86400 次请求的总量,这对服务的长期稳定性就是个实实在在的压力测试。

从我实际经验看,低频压测真正考验的不是服务端扛并发的能力,而是两点:一是请求节奏的控制精度——能不能真的做到 1 秒 1 次,而不是忽快忽慢;二是长时间运行中的资源监控——比如你需要在压测的同时盯着 GC 日志、连接池活跃数、文件描述符数量。这两点,恰恰是 JMeter 这类工具最容易被用错的地方。

2. 实现每秒1次请求的两种主流方案

2.1 方案一:单线程 + Constant Timer(恒定定时器)

这是最直观、也最容易理解的方案:把线程数设为 1,在取样器前后加一个恒定定时器,让每个请求之间固定间隔 1000 毫秒。

具体配置方式是这样的:

  1. 添加线程组(Thread Group):线程数填 1,Ramp-Up 填 0,循环次数按你需要发送的总次数填。比如要跑 1 小时就是 3600 次。
  2. 添加 HTTP 请求取样器:填写协议、服务器地址、端口、路径、请求方式、参数。
  3. 在 HTTP 请求下面添加定时器:Constant Timer,线程延迟(Thread Delay)填 1000 毫秒。
  4. 运行测试。

这里要说明一下 Constant Timer 的作用时机。JMeter 的定时器是在“取样器执行之前”生效的——每个取样器在发出请求前,会先等待定时器设定的时间。所以你把 Constant Timer 放在 HTTP 请求的同级或子级,它就会让这个请求每 1000 毫秒执行一次。

循环次数怎么算?很简单:循环次数 = 期望跑的总秒数。跑 10 分钟就是 600 次,跑 24 小时就是 86400 次。加上线程组里“循环次数”这个参数本身支持无限,你也可以填“永远”,然后用调度器里的持续时间来控制总时长。

这个方案的优点:配置简单,逻辑清晰,任何人一看就懂。缺点:Constant Timer 是一个“死等”定时器,它不关心上一次请求花了多长时间,只负责在每次取样器执行前固定等待 1000 毫秒。这里有个细节很多人会忽略——如果你在一个线程组里放了多个取样器,同一个 Constant Timer 会对每一个取样器都生效,导致时间被成倍拉长。所以用这个方案时,最好保持线程组里只有一个 HTTP 请求取样器,或者用作用域仔细控制定时器只对目标取样器生效。

2.2 方案二:Constant Throughput Timer(恒定吞吐量定时器)

如果说 Constant Timer 是个“笨办法”,那 Constant Throughput Timer 就是更贴合“每秒 1 次”这个目标的方案。它不是固定等待,而是动态计算需要等待的时间,以保证整体吞吐量维持在设定值附近。

配置方式:

  1. 在 HTTP 请求下添加定时器:Constant Throughput Timer。
  2. Target Throughput 填 60.0,单位是“每分钟请求数”。每秒 1 次 = 每分钟 60 次。
  3. Calculate Throughput based on 这一项选“this thread only”,因为在我们的场景里只有一个线程。

这里有三个选项需要理解:

  • this thread only:只按照当前线程来维持吞吐量。适合单线程场景。
  • all threads in current thread group:按当前线程组所有线程的总吞吐来计算。
  • all threads in current JVM:按整个 JVM 里所有线程的总吞吐来计算。如果有多个线程组同时跑,选这个可以让它们共同分摊目标吞吐。

我为什么建议用 this thread only?因为我们的目标就是“这一个线程每秒发 1 次”。如果选到其他两个选项,JMeter 会根据总的活跃线程数动态调整等待时间,一旦你后续加了线程,节奏就会被重新计算,反而不好控制。

Constant Throughput Timer 的原理是基于吞吐量目标来倒推每一次请求间的等待时间。它内部会记录线程启动后的累计请求数和经过的时间,然后计算出一个需要的延迟。好处是即便某个请求耗时较长,后续请求也会自动微调,保证单位时间内总请求数趋近目标值。

这跟 Constant Timer 的区别我用一个表来说明:

对比项Constant TimerConstant Throughput Timer
控制目标请求间隔固定单位时间请求数固定
是否受请求耗时影响不受影响,固定等待会根据耗时自动微调
精度间隔精度高,但实际吞吐会有波动吞吐精度高,适合“每秒X次”的目标
配置复杂度低中
多线程场景适配需要小心作用域支持按线程组/JVM聚合计算

2.3 两种方案怎么选

我的建议很简单:如果需求明确是“每秒 1 次、连续跑很久”,优先用 Constant Throughput Timer。因为它直接面向吞吐量这个目标,长时间运行时能自我修正节奏,不会因为某次请求响应慢导致后面的请求被耽误。

但是 Constant Timer 也不是一无是处。如果你要模拟的是“精确的固定间隔”,比如每 1000 毫秒必须发一次,不管上一次返回了没有,那么 Constant Timer 更合适——因为它是硬等,不受请求耗时影响,间隔非常稳定。

实际工作中我是怎么做的?如果是几分钟的短测试,随便选哪个都行,差异不大。如果是几小时甚至几天的长跑测试,我默认用 Constant Throughput Timer,配合非 GUI 模式跑。后面我会详细说非 GUI 模式下怎么跑。

这里还有一个进阶方案值得提一句:如果你用 JMeter 插件管理器的 Arrivals Thread Group,可以用“每秒到达 1 个请求”这种方式来建模,概念上更像业界负载模型里的 Arrival Rate。它跟官方线程组的区别是:官方线程组是“从线程池里取线程执行”,Arrivals 是“按到达速率动态创建迭代”。不过 Arrivals Thread Group 对新手来说有点复杂,而且依赖第三方插件,我在只需要 1 个线程的场景下一般不引入,避免增加环境的不确定因素。先把官方内置的两种方案玩明白,绝大多数场景都够用了。

3. 完整实操:从新建测试计划到验证频率

3.1 第一步:创建线程组与HTTP请求

打开 JMeter,默认会有一个测试计划(Test Plan)。先给它起个名,比如“每秒1次请求-长稳测试”。接着开始搭结构。

右键测试计划 -> 添加 -> 线程(用户) -> 线程组。在线程组面板里这样填:

  • 线程数:1
  • Ramp-Up 时间(秒):0
  • 循环次数:填一个足够大的数,或者勾选“永远”,配合调度器的持续时间来控制总时长

Ramp-Up 这里我必须强调一下:单线程场景下 Ramp-Up 填 0 是对的,因为只有一个线程,不需要拉长启动时间。如果你填了 1 秒,JMeter 会在 1 秒内启动这个线程,但因为它只有 1 个,实际启动几乎是瞬时的,对结果没影响。但如果你线程数填了多个,Ramp-Up 就很重要了——比如 10 个线程 Ramp-Up 填 10 秒,那就是 1 秒启动一个线程。我们这里不需要,所以填 0。

然后添加 HTTP 请求取样器。在 Thread Group 上右键 -> 添加 -> 取样器 -> HTTP 请求。这里要填的东西不用我啰嗦,域名、端口、路径、方法、参数,跟你在 Postman 里填的一样。需要注意一点:如果你要测的是 HTTPS 接口,JMeter 需要先处理证书问题,这个不在本文范围内,但遇到的时候要知道是证书问题而不是脚本问题。

3.2 第二步:配置定时器

以我推荐的 Constant Throughput Timer 为例:

在 HTTP 请求上右键 -> 添加 -> 定时器 -> Constant Throughput Timer。填两项:

  • Target Throughput:60.0
  • Calculate Throughput based on:this thread only

就这么简单。跑起来之后,理想状态下每秒 1 次请求会自动维持。

如果你用的是 Constant Timer,那就在 HTTP 请求上右键 -> 添加 -> 定时器 -> Constant Timer,把 Thread Delay 填 1000。

这里插一句关于定时器作用域的提醒:JMeter 定时器有个让人头疼的层级规则。如果你把定时器放在 HTTP 请求的子级,它只对该请求生效;如果放在线程组这一级,它对该线程组下的所有取样器生效。所以,当一个线程组里有多个 HTTP 请求时,别把定时器放在线程组层级,否则每个请求都会等待定时器的时间,总耗时会被“取样器数量 × 定时器时间”放大。这是一个非常经典的坑,我见过不少人在多请求场景下配了 Constant Timer,结果吞吐量比预期低了好几倍,查了半天才发现是定时器作用域搞的鬼。

3.3 第三步:验证请求频率是否准确

配置完了,怎么知道自己真的做到了每秒 1 次?光凭感觉不行,得用数据说话。

最简单的方式:添加一个“查看结果树”监听器,跑一小会儿,看请求时间戳。JMeter 在启动测试时,每个请求都会有时间戳。如果你是在 GUI 模式跑,直接看结果树里的请求列表,两个相邻请求的启动时间差应该稳定在 1000 毫秒上下。但结果树监听器本身很耗资源,长时间跑的时候不能开它。

更专业的验证方式是用简单数据写入器(Simple Data Writer)把采样结果落盘,然后检查日志文件的时间戳。具体操作:右键线程组 -> 添加 -> 监听器 -> 简单数据写入器,文件名填一个 .jtl 后缀的文件。跑完用 Excel 或者命令行查看这个文件,里面每一行是一条采样记录,有 timeStamp 字段,也就是请求启动时的毫秒时间戳。把相邻两行的时间戳相减,得到的应该就是 1000 毫秒左右。如果偏差超过 100 毫秒,说明你的节奏有问题,需要回去检查定时器配置。

我再分享一个自己常用的土办法:写一个 JSR223 后置处理器,在每个请求结束后用 System.currentTimeMillis() 打印当前时间,然后在日志里看相邻两个请求的时间差。这个方法在 GUI 模式下很直观,适合刚开始调试脚本时用。等脚本磨合成型了,再用简单数据写入器做正式记录。

3.4 非GUI模式跑起来更省资源

压测跑到最后,一定要用非 GUI 模式。原因很简单:GUI 模式本身会消耗大量内存和 CPU,尤其是在开着各种监听器(结果树、聚合报告、图形结果)的情况下。你用一个占着资源的工具去测性能,测出来的数据能准吗?

非 GUI 模式启动命令长这样:

jmeter -n -t /path/to/test.jmx -l /path/to/results.jtl -j /path/to/jmeter.log

参数说明:

  • -n:非 GUI 模式
  • -t:指定测试脚本文件
  • -l:指定结果日志文件
  • -j:指定 JMeter 运行日志文件

如果你配置了调度器的持续时间,测试会在时间到后自动结束。如果没有,等所有循环跑完也会结束。长稳测试建议在脚本里配好持续时间,这样不用人守着。

在 Linux 服务器上跑长稳测试,我还习惯用 nohup 放到后台执行:

nohup jmeter -n -t plan.jmx -l result.jtl -j run.log > /dev/null 2>&1 &

跑完之后,用 tail -f run.log 可以实时观察执行进度,看到“end of run”字样就说明跑完了。

这里有个细节:JTL 文件会越写越大,长跑 24 小时的话可能产生几百 MB 的数据。建议在测试计划里配置结果采样策略,只记录必要的信息。我一般用 -J 参数按需开启字段,比如记录线程数、时间戳,但关闭响应体等大字段,减少落盘数据量。

4. 压测中的灵魂:断言、响应查看与报告导出

4.1 JSR223 / Beanshell 断言实战

频率控制好了,请求发出去了,怎么知道每次请求到底成功没有?只看 HTTP 状态码 200 远远不够。很多接口在业务层返回的 HTTP 200 但 body 里带着错误码,这时候就需要断言。

JMeter 自带的“响应断言”可以检查响应文本是否包含某个字符串,但它能力有限——一旦需要做逻辑判断(比如同时校验多个字段、根据时间做条件判断、取出某个值再跟预期比较),就得写脚本了。

JMeter 支持 Beanshell 和 JSR223 两种脚本方式。我的建议是:优先用 JSR223 + Groovy,不要用 Beanshell。原因很简单:Beanshell 在 JMeter 中的执行效率比较低,高频场景下会拖慢测试节奏;而且 Groovy 语法更现代、更好写。虽然我们这里才每秒 1 次,Beanshell 性能影响不大,但养成好用 JSR223 的习惯总是对的。

一个典型的 JSR223 断言脚本长这样(放在 HTTP 请求下的 JSR223 断言里):

// 获取响应内容 def response = prev.getResponseDataAsString() def status = prev.getResponseCode() // 1. 状态码必须是200 if (status != "200") { AssertionResult.setFailure(true) AssertionResult.setFailureMessage("状态码异常: " + status) return } // 2. 业务code必须是0 def json = new groovy.json.JsonSlurper().parseText(response) if (json.code != 0) { AssertionResult.setFailure(true) AssertionResult.setFailureMessage("业务错误: code=" + json.code + ", msg=" + json.msg) return } // 3. 响应时间不能超过3秒 if (prev.getTime() > 3000) { AssertionResult.setFailure(true) AssertionResult.setFailureMessage("响应超时: " + prev.getTime() + "ms") }

注意脚本里用的 prev 对象,它是 SampleResult 的引用,通过它可以拿到响应数据、状态码、耗时等。AssertionResult 是断言结果的引用,调用 setFailure 即可标记失败。

我还用了一个实战技巧:把从响应里解析出来的关键字段保存成 JMeter 变量,供后续请求使用。比如从登录接口响应里取出 token,存在 vars 里:

def json = new groovy.json.JsonSlurper().parseText(prev.getResponseDataAsString()) vars.put("token", json.data.token)

之后的请求里就可以用 ${token} 引用。这在构造有依赖关系的接口链路时非常有用。

4.2 压测过程中实时查看响应内容

很多人问我:在 Linux 上跑 JMeter 压测,怎么实时看接口返回了什么?GUI 模式当然可以开结果树,但服务器上通常没有图形界面,而且开着结果树会影响压测结果。

我的做法有三个层次:

第一层:临时查看,用“视图结果树”只在小范围、短时间调试时开,确认脚本格式和请求参数没问题后立刻关掉。

第二层:把完整响应数据写入文件。在 HTTP 请求下添加一个“保存响应到文件”后置处理器,配置好保存路径和文件名前缀。这样每次请求的响应都会落盘,压测完直接去看文件内容。文件数量会随着请求数量增长,所以这个方案适合短时间调试,不适合长测。

第三层:在 JSR223 后置处理器里做条件保存。比如只把断言失败或响应异常的请求内容保存下来:

def body = prev.getResponseDataAsString() if (prev.getResponseCode() != "200" || prev.getTime() > 3000) { def f = new File("/tmp/error_" + System.currentTimeMillis() + ".txt") f.text = body }

这个做法的好处是只保留异常样本,文件量很小。跑完长测后,直接 ls /tmp/error_*.txt 就能看到所有异常请求的响应内容,排查问题效率极高。脚本里注意加个时间戳后缀,避免多个请求写到同一个文件互相覆盖。

4.3 一条命令生成HTML测试报告

JMeter 从 3.0 开始支持直接生成 HTML 报告,这是个被低估的功能。很多人压测完了只知道看 .jtl 文件里的原始数据,其实 JMeter 自带的报告生成器能产出一套完整的可视化报告,包含吞吐量曲线、响应时间分布、错误率、活跃线程数等图表,特别适合用来做团队汇报。

前提条件:跑测试的时候,必须记录足够多的数据字段,否则报告里的很多图表是空的。建议用这样的命令启动测试:

jmeter -n -t plan.jmx -l result.jtl \ -Jjmeter.save.saveservice.response_data=false \ -Jjmeter.save.saveservice.samplerData=false \ -Jjmeter.save.saveservice.requestHeaders=false \ -Jjmeter.save.saveservice.responseHeaders=false \ -Jjmeter.save.saveservice.thread_counts=true \ -Jjmeter.save.saveservice.timestamp=true

简单说:像响应体、请求体这种体积大的字段别存,不然 .jtl 文件会爆炸;线程数、时间戳这种报告必须的字段一定要存。

跑完之后生成报告:

jmeter -g result.jtl -o /path/to/report/dir

-g 指定结果文件,-o 指定输出目录。输出目录必须不存在或者是空目录,否则会报错。这是 JMeter 的一个不太友好的设定,我第一次用就被它坑过。

生成的 index.html 用浏览器打开就能看,里面有很多图表区域。我个人最常用的是这三个:

  • 吞吐量随时间变化图:验证每秒请求数是否稳定在 1 左右。
  • 响应时间百分位图:看 P90、P95、P99 的响应时间有没有突发尖峰。
  • 活跃线程数图:确认线程数量在整个测试期间没有异常波动。

5. 常见问题与排查技巧实录

5.1 频率不准确的几大原因

我在实际测试中遇到过很多次“明明配了 1000 毫秒,结果实际间隔不是 1 秒”的情况。归结起来原因就这么几个:

第一,GUI 模式对节奏的影响最大。JMeter 本身需要分配资源去处理界面刷新和监听器数据展示,每一次取样器执行完,监听器都要更新界面,这会挤压定时器的执行精度。所以为什么我一直强调非 GUI 模式——不只是为了省资源,更是为了保证定时器按预期工作。

第二,脚本里存在额外的处理逻辑。如果你加了响应断言、JSR223 后置处理、正则表达式提取器之类的组件,这些组件的执行时间会计入取样器的总时间。而 Constant Throughput Timer 是通过控制吞吐量来反向调整等待时间的,它会把所有额外处理时间都算进去,然后压缩定时等待时间,导致整体的请求间隔被调整。这不是 bug,是它的设计逻辑,你要理解它才会用对它。

第三,目标吞吐量设置错误。每秒 1 次 = 60 次/分钟,这个换算很多人会搞错。如果你填了 1.0,那就变成每分钟 1 次,也就是 60 秒 1 次,完全混了。我在项目里见过同事把 60 填成 600,压出来的流量直接翻了 10 倍。这个错误很常见,因为 Constant Throughput Timer 的单位是每分钟,不是每秒。

第四,JVM 环境问题。比如 GC 停顿会导致请求间隔忽大忽小,尤其是在堆内存配置不合理的情况下。长稳测试前,确认 JMeter 自己有足够的内存:启动脚本里给 HEAP 参数留够空间。JMeter 默认的堆内存只有 1GB,长跑大量采样记录时很容易触发 GC。建议修改 bin/jmeter 脚本里的 HEAP="-Xms1g -Xmx2g" 或者更大。

5.2 定时器之间的区别与误用

JMeter 的定时器家族很庞大,除了本文提到的 Constant Timer 和 Constant Throughput Timer,还有高斯随机定时器、均匀随机定时器、同步定时器(Synchronizing Timer,用来制造瞬间并发)、BeanShell 定时器、JSR223 定时器等。

新手最容易混淆的是这几个:

  • Constant Timer:固定等待,不关心前面发生了什么。
  • Gaussian Random Timer:在指定均值和标准差之间随机等待。比如均值 1000ms、标准差 100ms,那等待时间大致在 900~1100ms 之间浮动。用于模拟更真实的人类操作节奏。
  • Uniform Random Timer:类似高斯,但随机分布是均匀的。
  • Synchronizing Timer:让多个线程在某个点集合,然后同时释放。这个是用来制造并发峰的,跟“每秒 1 次”的场景完全相反。

如果你要模拟“真实用户每隔一秒操作一次”这种场景,用 Gaussian Random Timer 比 Constant Timer 更贴近现实,因为真实用户的间隔不可能是机器般精确的。但如果你的目标是“严格每秒 1 次”(比如验证限流规则),那就必须用 Constant Timer 或 Constant Throughput Timer,不能引入随机性。

还有一个容易踩的坑:同步定时器出现在线程组里时,会让所有线程卡在集合点互相等待,如果你一共就 1 个线程,它也会等——等到超时为止。有人把同步定时器从之前的并发测试脚本里复制过来,忘了删,结果压测一直卡住,请求发不出去。排查时先检查线程组下面所有组件,尤其是这种“看不见”的定时器。

5.3 Linux下压测的几个坑

服务器上跑压测,有一些跟 Windows 上不一样的问题。我捡几个典型的说。

第一个是时间同步。如果你的测试脚本依赖系统时间来做断言或记录,那压测机和被压测的服务器之间要做好时间同步,否则你对比两边日志时会对不上。

第二个是文件句柄限制。跑长稳测试时,JMeter 每发一次请求就要建立一个连接(取决于你用的是否是 keep-alive),如果被压测服务的连接不释放,JMeter 侧的文件描述符会持续增长。Linux 默认的 ulimit -n 可能只有 1024,长跑之后 JMeter 会报 “Too many open files”。跑之前先 ulimit -n 65535 或者更大。

第三个是 nohup 与 jmeter 进程的关系。在 Linux 上用 nohup 启动 JMeter 后,如果你直接关掉终端,进程可能会被挂断信号杀掉。虽然 nohup 理论上会忽略挂断信号,但某些环境下还是会有问题。更稳妥的做法是用 setsid 或者 screen/tmux 跑,尤其是长时间无人值守的测试。

第四个是查看响应内容时别用“查看结果树”。服务器上没有图形界面,你开了结果树它要么没法显示,要么会占用大量内存。用前面提到的“保存响应到文件”或者 JSR223 条件保存更实用。

5.4 遇到问题怎么办:排查思路

压测中的问题五花八门,但排查思路大致是固定的。我自己习惯按这个顺序来:

第一步,先确认 JMeter 这一侧没有锅。看 jmeter.log 有没有报错,看取样器的响应码和响应信息。很多“压测不行”的问题,本质上是脚本的问题——参数没传对、鉴权没过、请求头少了字段。

第二步,确认节奏正确。用简单数据写入器的 .jtl 时间戳做检查,相邻请求间隔是否稳定。如果节奏乱了,回查定时器配置和作用域。

第三步,确认被压服务没被打挂。看服务端日志、监控指标、连接数、GC 情况。如果是长跑测试,重点看内存曲线是否随时间缓慢爬升——那才是低频长测最想抓的问题。

第四步,确认网络链路正常。压测机和目标服务之间有没有防火墙、负载均衡、限流组件拦截。尤其注意:很多网关默认会对高频请求做限流,但低频请求也可能被一些“慢速攻击防护”策略拦截,因为它觉得你“频率太低像扫描”。这个我在一个客户现场踩过坑,1 秒 1 次的心跳请求被防护设备识别成慢速扫描给拦了,对方还以为是我们压测代码写错了。

6. 个人体会与小技巧

做了几年性能测试,我自己的体会是:低频压测比高频压测更需要耐心,也更考验细节。高频压测出了问题,几分钟就能看出来;低频长测出了问题,可能要在几小时甚至几天后才暴露。所以跑低频长测之前,一定要把脚本、定时器、断言、日志策略全部打磨清楚,宁可先跑 10 分钟短的确认无误,再上完整的时长。我见过有人直接拿一个没验证过的脚本跑了 24 小时,第二天醒来发现请求路径都是错的,整个测试白做——这种事一次就够长了记性。

最后分享一个小技巧:JMeter 跑低频长测时,不要在脚本里放太多监听器。监听器每处理一条采样数据都会消耗资源,低频场景下虽然流量不大,但如果监听器写文件过于频繁,磁盘 IO 也可能成为瓶颈。我一般只保留简单数据写入器,或者干脆去掉监听器,只用 jmeter.properties 里的内置 summary 输出。summary 每隔一段时间会在 jmeter.log 里打印一次汇总,包含当前请求数、错误数、吞吐量等,用 tail -f 盯这个就够了。

再补一句:如果你要测的目标是“验证接口在低频请求下的响应时间稳定性”,除了看平均响应时间,建议重点看 P99 和响应时间的方差。低频请求的场景里,响应时间突然从 200ms 跳到 5 秒往往是连接池重建、缓存过期、懒加载这类问题的信号。把响应时间变化曲线和 GC 日志放在一起看,能定位到很多有意思的问题。

这就是我在这类“每秒 1 次”压测任务里的全部实践经验。方案不一定是最优的,但都是我验证过、在项目里真正跑过的路子。大家在自己环境里跑的时候,遇到什么奇怪的问题,欢迎交流。毕竟压测这东西,纸上谈兵没用,只有自己跑过的数据才最有说服力。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 14:05:29

WinCC报表实现全攻略:从打印作业到VBS脚本导出Excel

接手项目的时候,对方提了一句"画面都挺满意,就是每天要手工导数据做报表,太痛苦了"。这句话让我意识到,很多WinCC项目做到最后,实时画面只是及格线,真正决定运营方用着顺不顺手的,往往…

作者头像 李华
网站建设 2026/9/29 14:02:34

TensorFlow安装与生产部署的底层原理与避坑指南

1. 这不是“装个库”那么简单:TensorFlow到底在解决什么问题?你搜“tensorflow”,首页跳出来的不是技术文档,而是“TensorFlow安装失败”“ImportError: No module named tensorflow”“pip install tensorflow超时”——这说明什…

作者头像 李华
网站建设 2026/9/29 14:02:05

从TMS320F28335到国产DSP:选型对比与迁移实战指南

做了几年的嵌入式软件开发,手头好几个项目都在处理TI DSP的国产替代工作。最开始大家只是把国产芯片当作备选方案,没想到这几年越做越深入,从电源、电机驱动到并网逆变器,甚至高端音频设备,都在问同一个问题&#xff1…

作者头像 李华
网站建设 2026/9/29 14:00:32

IIPDS 2026视角下的图像与数据科学交叉:超分、修复与遥感医学实战

1. 从IIPDS 2026的征稿方向看图像与数据科学的交叉地带第一次看到“2026年图像、信息处理与数据科学国际会议(IIPDS 2026)”这个标题,我脑子里蹦出来的第一个念头是:这三个词终于被放到同一张桌子上了。过去几年,图像处…

作者头像 李华
网站建设 2026/9/29 14:00:08

Win10运行bash的三种方案:Git Bash、WSL1与WSL2选型指南

1. 先厘清一个根本误区:Win10里根本没有原生“bash批处理命令”很多人搜“Win10如何使用bash批处理命令”,一上来就卡在概念上——这个说法本身就不成立。Windows 10 的原生命令行环境是cmd.exe和后来升级的PowerShell,它们用的是 Windows 自…

作者头像 李华
网站建设 2026/9/29 13:55:57

阿里Qwen-Image LoRA训练指南:从零跑通风格定制模型

简介:这份资源是面向多模态模型开发者与AI训练爱好者的Qwen-Image LoRA训练实战代码包,聚焦阿里开源20B模型的三层融合架构解析与LoRA适配落地,帮助读者解决中文场景下微调效率低、手脚生成异常等实际问题。压缩包共5个文件,约12K…

作者头像 李华