简介:这是一套基于 JMeter 3.3 扩展实现 RabbitMQ 压力测试的完整资源包,面向需要开展消息队列性能验证的测试工程师、开发人员及运维人员。针对 RabbitMQ 采用 AMQP 协议、而 JMeter 原生不支持该协议的问题,资源整合了必要的扩展 jar 包与配置方案,并提供可直接参考的测试脚本与运行脚本,帮助读者快速搭建从连接配置、消息发布到结果监控的压测环境。资源共 2000 个文件,核心类型包括 html 帮助文档、png 界面说明图、js/css 前端资源、jar 扩展库、jmx 测试计划,以及 bat/sh 启动与辅助脚本等,压缩包大小约 51.92MB,目录结构完整,便于按功能模块检索。已有 1085 人学习下载。对于希望摆脱商业压测工具限制、在 JMeter 3.3 版本上落地 RabbitMQ 并发测试的读者,这套资源能提供扎实的配置参考与实战基础,覆盖插件整合、AMQP 连接参数设置、线程组与采样器设计、监听器分析等关键环节,有助于理解高并发下 RabbitMQ 的吞吐与延迟表现。
1. 追加压测的起点:为什么是JMeter 3.3 + RabbitMQ
1.1 这个场景解决什么问题
老项目中一直在用 JMeter 3.3 做 HTTP 接口压测,某天业务方提了个需求:需要把一批消息从原来的同步接口调用改成异步 MQ 投递,RabbitMQ 作为消息中转的核心组件。上线前要回答一个问题:当前的 RabbitMQ 集群配置到底能扛住多大的消息生产速率?消费端如果处理不过来,积压会不会拖垮 Broker?
于是有了这笔“追加投入”——在已有 JMeter 3.3 基础上,额外补一套针对 RabbitMQ 的压测能力,而不是单独引入 k6 或者写一套生产者脚本去压。原因很朴素:团队已经熟悉 JMeter 的操作习惯,报告体系也挂在现有 CI 上,最好用同一套工具把消息中间件的性能数据也纳进来。
这种“追加”在实际工作中非常典型。不是每个项目都有条件新起一套压测平台,很多时候是在现有测试资产上做扩展。JMeter 3.3 虽然是老版本,但它的 AMQP 插件生态其实已经非常成熟,配合 RabbitMQ 的 Management HTTP API,可以完成从生产者发消息到消费者处理、再到队列积压观测的完整闭环。
1.2 技术选型的底层逻辑
先解释一个大家容易纠结的问题:JMeter 能压 RabbitMQ 吗?答案是能,但不是 JMeter 原生支持,而是通过插件实现的。
JMeter 本身是一个基于 Java 的通用压测框架,核心设计是“Sampler”机制。不同的协议通过不同的 Sampler 扩展接入,HTTP、JDBC、FTP 这些都是内置的,而 AMQP 0-9-1 协议则需要借助社区插件(比如 jmeter-amqp-plugin 或 JMeter-RabbitAMQP 库)来提供 RabbitMQ 的生产者/消费者 Sampler。
选择 JMeter 3.3 而不是最新版本,有两点实际考量。第一点是兼容性:老项目里的测试脚本、监听器配置、JTL 报告解析逻辑全部是基于 3.3 做的,升级主版本意味着回归所有存量压测资产,成本大于收益。第二点是稳定性:3.3 在 Java 8 环境下运行非常稳定,而 AMQP 插件对 Java 版本不敏感,它只是通过 RabbitMQ Java Client 与 Broker 通信,插件的核心逻辑不依赖 JMeter 新特性。
这里补充一句,如果你是新项目,完全可以考虑 k6 或者直接用 RabbitMQ 官方性能测试工具 PerfTest。但如果你和我一样是“在老工具上追加新协议”,那 JMeter 3.3 + amqp-plugin 的组合就是性价比最高的方案。
2. 准备阶段:JMeter 与 RabbitMQ 环境搭建
2.1 JMeter 3.3 安装与 JDK 配置
JMeter 3.3 的安装本身不复杂,但有几个细节容易踩坑。首先明确前提:JMeter 3.3 要求 JDK 8 或以上版本,推荐使用 8u144 之后的版本,老版本 JDK 在某些高并发场景下会出现线程池分配异常的问题。
操作步骤如下:
- 下载 JDK 8 并配置环境变量 JAVA_HOME,路径精确到 JDK 安装目录,不要包含 bin 子目录。
- 下载 JMeter 3.3 的 apache-jmeter-3.3.zip 包,解压到纯英文路径,避免中文目录导致 Classpath 异常。
- 修改 jmeter.bat(Windows)或 jmeter.sh(Linux)中的堆内存参数。默认的 HEAP="-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m" 在压测时不够用,建议改成:
HEAP="-Xms2g -Xmx4g -XX:MaxMetaspaceSize=512m"这个调整非常关键。RabbitMQ 压测和 HTTP 压测不同,它会产生长连接、信道、消息缓存等大量堆内对象,如果堆太小,压测中途会出现频繁 Full GC,导致吞吐曲线出现锯齿状波动,严重影响压测数据的可信度。
- 运行 jmeter.bat 验证
Command: java -version输出的是 JDK 8 版本,同时在 JMeter 的 Options -> Log Viewer 里确认无异常信息。
2.2 本机部署 RabbitMQ 与启用 Web 管理界面
RabbitMQ 的启动问题在 Windows 上尤其多。我的建议是:如果是压测环境,直接使用 Docker 部署,能省掉 Erlang 版本匹配的坑。但考虑到有些测试机在隔离网络环境,没有 Docker Registry 可用,我两种方式都列一下。
Docker 方式一条命令搞定:
docker run -d --name rabbitmq \ -p 5672:5672 -p 15672:15672 \ -e RABBITMQ_DEFAULT_USER=admin \ -e RABBITMQ_DEFAULT_PASS=admin123 \ rabbitmq:3.9-management注意这里必须带 management 标签的镜像,否则没有 Web 管理界面,后续观察队列积压会很麻烦。
手动安装方式(Windows)需要踩的坑是 Erlang 和 RabbitMQ 的版本对应关系。例如 RabbitMQ 3.8 要求 Erlang 23.2 以上,而 RabbitMQ 3.9 要求 Erlang 24 以上。如果版本不匹配,服务启动时会在后台日志中直接报Failed to start erlang node之类的错误。
启动完成后,浏览器访问http://localhost:15672,使用配置的账号密码登录。这一步不仅是验证服务可用,更是为了后续压测时实时查看队列状态。我建议在压测过程中单独开一个浏览器窗口,固定显示 Management 界面的 Queues 页面,这样消息是否积压一眼就能看出来。
2.3 确认 RabbitMQ 与 JMeter 联调基线
环境准备好后,不要急着写压测脚本。先用官方自带的方式做一次链路连通性验证。
在 RabbitMQ 管理界面中点击 Queues 页签,新建一个名为test.ping的队列。然后打开管理界面的 Exchanges 页签,找到默认的 AMQP default exchange,在 Publish message 区域往test.ping队列发一条 hello 消息。回到 Queues 页面点击 Get messages,确认消息能正常消费。
这一步的意义是确保 Broker 本身没问题。如果这里都失败了,那问题一定出在 RabbitMQ 安装配置上,而不是后续的 JMeter 脚本。
接下来在 JMeter 侧建立最简单的 AMQP 连接测试。下载 jmeter-amqp-plugin(一般选择支持 3.x 的 1.4 版本),将插件包里的 JAR 放入 JMeter 的 lib/ext 目录,重启 JMeter。然后在线程组下添加一个 AMQP Publisher,填好 host、port、username、password,不做任何循环,先发一条消息看看 Management 界面的消息计数是否有变化。
从我的经验看,这一步通常能排查出三类问题:插件 JAR 冲突(lib 目录下残留旧版本)、RabbitMQ 端口没有放通(服务器防火墙拦了 5672)、账号权限不足(指定 vhost 不匹配)。这三个问题如果在基线验证阶段没排掉,后面压测结果基本没法看。
3. 压测脚本配置:从零到一打通 AMQP 链路
3.1 AMQP 插件装载与 Sampler 选择
AMQP 插件装好后,需要在 JMeter 的 Test Plan 里做以下配置:
- 添加一个线程组(Thread Group),线程数根据压测目标设置。比如前期摸底可以用 10 个线程、循环次数 1000,观察 TPS 和响应时间。
- 在线程组下添加 Config Element -> RabbitMQ Connection(部分插件版本叫 AMQP Connection)。这里需要填写:
- Host:RabbitMQ 所在 IP
- Port:默认 5672
- Username / Password:带对目标 vhost 的访问权限
- Vhost:默认是
/,如果业务方有独立 vhost 则按要求填写 - Heartbeat:建议设为 30,默认 60 在某些网络环境下容易断连
Connection 配置是全局共享的,多个 Sampler 可以复用同一个连接配置,JMeter 内部会维护连接池,避免每次发消息都重新建立 TCP 连接,这会极大拉低压测性能。
- 线程组下添加 Sampler -> AMQP Publisher。这里重点设置:
- Exchange:一般不用自定义,直接用默认 exchange,此时 Routing Key 就是队列名称。
- Routing Key:目标队列名,比如
order.create - Delivery Mode:PERSISTENT(2)还是 NON_PERSISTENT(1)。压测生产者时建议先用非持久化,因为持久化消息涉及磁盘刷盘,性能差异能到数倍。先测出理论吞吐上限,再叠加持久化找到带业务保障的阈值。
- Message Type / Headers:如果业务方使用的是 Spring AMQP 或 RabbitTemplate,消息头里通常会带
__TypeId__之类的属性。压测数据必须模拟真实的 headers,不然消费者反序列化会报错。
- 添加 Listener -> View Results Tree 和 Summary Report。压测期间最好关闭 View Results Tree,这个监听器会保存每个 Sampler 的完整响应数据,高并发时是巨大的 IO 和内存开销,影响测试结果准确性。建议先开一次验证脚本正确性,正式压测时只保留 Summary Report 和 Backend Listener。
3.2 生产端压测脚本的细节打磨
生产者压测最容易犯的错是“只测了发送能力,没测真实业务”。真实场景里,消息体不是定长字符串,而是包含订单号、用户 ID、商品信息、时间戳等业务字段的 JSON。压测数据需要做参数化,否则消息体完全一致,不仅不符合真实场景,还会因为字符串驻留导致内存效率虚高。
参数化的做法是:
- 在 Test Plan 中添加 Config Element -> CSV Data Set Config,指向一个预先生成的压测数据文件,字段包含
orderId、userId、skuId、amount等。 - 在 AMQP Publisher 的 Message Body 中使用
${orderId}${userId}形式的占位符,拼接出最终的 JSON 消息体。 - 如果需要对消息体中的时间字段做动态化处理,可以在脚本中添加 BeanShell 或 JSR223 预处理,用 System.currentTimeMillis() 生成时间戳。这里注意优先使用 JSR223 + Groovy,BeanShell 在 3.3 版本中的性能表现不佳,高并发下会成为瓶颈。
除了参数化之外,还有一个细节是 Publish Return 和 Confirm 模式的设置。AMQP Publisher 提供了Use Publisher Confirms选项,开启后每条消息都要等 Broker 返回 ack 才算成功。这个开关直接影响 TPS 指标——不开 Confirm,JMeter 只管发送不管结果,TPS 看起来会虚高;开了 Confirm,结果更真实,但 TPS 会明显下降。
这就引出一个关键问题:压测目标必须和生产端的配置对齐。如果线上生产环境是通过 spring-rabbit 的 publisher-confirm-type 开了 Confirm 的,那压测就必须开 Confirm;如果线上是 fire-and-forget 的直发模式,压测就不开 Confirm。否则压出来的数据对线上没有任何参考意义。
3.3 消费端吞吐与积压监控的设计
生产端压测做完后,消费端的压测同样重要。消费端压测的核心指标不是 TPS,而是消费速率能否跟得上生产速率,以及 Broker 上的堆积数量是否线性增长。
Jmeter AMQP 插件提供 AMQP Consumer Sampler,可以模拟消费者从指定队列拉取消息。但这里有个需要注意的点:JMeter 的 Consumer Sampler 本质上是单线程轮询拉取,和真实业务中多个消费者并发处理的能力有很大差距。因此消费端压测通常配合 JSR223 断言处理器一起使用,模拟“拿到消息之后做业务处理”的耗时。
我的做法是:在 Consumer Sampler 后面加一个 JSR223 PostProcessor,里面用 Groovy 随机 sleep 10 到 50 毫秒,模拟业务处理耗时,然后用 System.currentTimeMillis() 记录处理前后的差值,写入 JMeter 变量,最终在 Summary Report 中观察消费侧的平均响应时间。
同时,积压监控是消费端压测的重点。监控途径有两个:
- 第一个是 RabbitMQ Management 页面实时刷新 Queues 页签,观察 Ready 和 Unacked 两个数字。
- 第二个是调用 Management HTTP API,定时采集队列深度:
curl -u admin:admin123 http://localhost:15672/api/queues/%2F/order.create | jq .messages_ready采集频率建议 5 秒一次,用脚本记录到文本文件,压测结束后整理成时间序列图表。如果队列 Ready 数量能保持在一个稳定水位,说明消费速率和生产速率基本匹配;如果 Ready 持续上涨且没有收敛趋势,说明消费者集群需要扩容,或者消费逻辑里存在阻塞瓶颈。
4. 压测执行的坑与排查实录
4.1 插件不生效与连接超时
压测前最尴尬的事是安装了 AMQP 插件,但右键添加 Sampler 时找不到 AMQP Publisher 选项。
这种问题 90% 是插件 JAR 放错位置或者版本冲突。JMeter 加载插件的目录是lib/ext,不是lib,而且插件名不能包含空格(部分老版本插件存在这个限制)。排查方法是打开 JMeter 的jmeter.log,搜索amqp或rabbit,看是否有 Failed to load 的报错。如果有ClassNotFoundException,大概率是插件 JAR 的依赖没有附带完整。
连接超时问题则集中在 RabbitMQ 侧。典型场景是 JMeter 脚本配了内网 IP,但 JMeter 运行在本地开发机,网络策略不允许跨网段访问 5672 端口。排查时先用 telnet 验证端口连通性:
telnet 192.168.1.10 5672如果 telnet 不通,而 RabbitMQ 服务本身正常,大概率是防火墙规则或安全组策略的问题。Windows 上还需要额外检查 Erlang 的分布式节点是否绑定了 hostname,如果 hostname 解析异常,RabbitMQ 虽然启动但监听端口可能没有绑定到 0.0.0.0。
4.2 压测过程中数据准确性问题
压测跑起来后,数据不准的坑比环境问题更隐蔽,也更致命。我遇到过最典型的情况是 TPS 曲线出现“平台期”——无论怎么加压,TPS 始终稳定在某个值上不涨。排除了后端瓶颈后才发现,瓶颈出在 JMeter 所在的压测机本身。
JMeter 3.3 在单机模式下,常规 HTTP 压测可以支撑数千并发。但 AMQP 压测的运行模型不一样,长连接是常驻状态,每增加一个线程就增加一个 AMQP connection,而每个 connection 会占用 Broker 端一个 Erlang 进程。当 JMeter 线程数超过 500 时,不仅压测机 CPU 会成为瓶颈,Broker 端的 Erlang VM 调度也可能出现不稳定。
解决思路有两个:
- 分布式压测:多台 JMeter 机器同时加压,由一台 Controller 汇总数据。
- 控制线程数:不要盲目加线程数,先梯度加压,比如 10、20、50、100、200,观察 TPS 的线性增长边界,找到最佳并发数。
另一个数据准确性问题是测试时间的取值。JMeter 的 Summary Report 默认统计的是从发送到收到 Broker ack 的完整 RTT,而业务关心的往往是 Broker 成功落盘的时间。这两者之间的差值受网络环境影响很大,如果压测机和 Broker 不在同一个机房,RTT 可能占到总响应时间的 60% 以上。我的做法是在 Broker 所在服务器上单独跑一个 RabbitMQ PerfTest 小样本测试,对比 JMeter 的结果,剔除网络开销带来的误差。
4.3 消息积压的误判场景
消费者压测做完后,业务方问了一个问题:为什么压测停止后,队列里还有积压?
这其实是正常的。压测停止意味着生产端不再发消息,但消费者进程还在运行,会把队列中的剩余消息消费完。真正需要关注的是两个指标:积压停止增长的时间点、以及积压清零的总耗时。如果压测停止后积压依然上涨,说明生产端有消息还在投递,需要检查是不是 JMeter 线程组没有完全停止,或者 RabbitMQ 里有其他生产者在写入。
还有一个容易误判的点是 Unacked 消息数量。AMQP 协议里,消费者拉取到消息后需要发送 ack 才算是消费成功。如果 JMeter 的 Consumer Sampler 配置了自动 ack(AutoAck=true),Unacked 基本不会增加;如果配置了手动 ack,且消费逻辑中遇到异常没有正确回复 nack 或 reject,Unacked 会持续上涨,最终导致整个队列被阻塞。排查这类问题时重点看 Unacked 数量的变化趋势,它比 Ready 数量更能反映消费端是否健康。
5. 压测结果分析与经验补充
5.1 关键指标怎么看
RabbitMQ 压测结束后,不要只看 JMeter 报告里的 TPS 和响应时间,需要结合监控指标做交叉验证。
我认为最核心的指标是消息吞吐量(throughput)和消息堆积量(queue depth)的对应关系。JMeter 报告只能告诉你“发出去多少条”,而 RabbitMQ Management API 能告诉你“实际处理了多少条、积压了多少”。两者之间的差额就是压测期间消息延迟的真实体量。
具体分析路径可以这样组织:
- 如果消息吞吐量上不去,优先看连接数和信道数。RabbitMQ 官方建议是连接数控制在单个 Erlang 进程可承受的范围内(一般建议数千级别),信道数可以比连接多。如果 JMeter 开了太多线程,每条线程独占一个 connection,可能会触发连接数瓶颈。
- 如果连接数正常但吞吐量低,看 CPU 和内存。RabbitMQ 是 Erlang 编写的,CPU 密集型操作(比如消息路由、ACK 处理)会消耗大量 Erlang VM 的调度资源。实测中当 CPU 使用率超过 70% 时,消息延迟会明显上升。
- 如果吞吐量稳定但积压持续上涨,问题在消费端而不是 Broker,这时候要去查消费者的处理逻辑,是数据库写入慢还是下游调用阻塞。
5.2 高并发下 JMeter 自身的参数调整
最后补充一些 JMeter 3.3 在高并发 AMQP 压测下的调优经验。
第一,关闭所有的图形化监听器。压测过程中我习惯只开一个 jtl 文件写入器或者 Backend Listener,图形界面一律在测试稳定后通过已有 jtl 结果回放查看。如果压测过程中必须实时观察,使用耐心等待模式(jmeter.sh -n -t script.jmx -l result.jtl)并用外部工具解析。
第二,调整系统网络参数。Linux 压测机上需要调整文件描述符限制:
ulimit -n 65535以及 TCP 连接复用相关参数,避免出现大量 TIME_WAIT 连接占用端口。Windows 压测机则需要调整动态端口范围:
netsh int ipv4 set dynamicport tcp start=10000 num=55535第三,JSR223 脚本一定要勾选 Cache compiled script。JMeter 3.3 支持对 Groovy 脚本做缓存编译,不勾选的话每次执行都会重新编译,脚本性能会有数量级的差距。这个选项隐藏在 JSR223 Sampler 的属性面板上方,很有迷惑性。
第四,如果压测目标是确认系统的最大并发数,不要用固定线程数持续压,而是用阶梯递增的方式。JMeter 的 Ultimate Thread Group 插件可以实现线程数的梯度增长和持续时间控制,先观察 TPS 随负载增加的变化曲线,找到拐点位置,这个拐点对应的并发数就是系统的极限承载能力。
我在实际项目中的体会是,用 JMeter 压 RabbitMQ 并不复杂,难点在于把压测指标跟业务场景对齐。每次压测前先问清楚三个问题:线上是同步发送还是异步确认?消息投递后消费者做哪些后续动作?业务方关心的核心指标是吞吐、延迟还是积压?把这三个问题的答案固化到测试脚本里,这套压测资产就能持续复用到后续的版本发布、集群扩容、配置调优等场景中。
本文还有配套的精品资源,点击获取