JMeter压测RabbitMQ完整指南:从环境搭建到踩坑实战
发布时间:2026/9/8 8:57:06 作者:尧图编辑部 阅读量:1,286

简介面向需要在分布式场景下验证RabbitMQ吞吐与稳定性的测试工程师这份资源提供了一套基于Apache JMeter的RabbitMQ生产/消费性能压测方案。压缩包内既有JMeter运行环境与相关插件也包含多组可直接复用的jmx测试计划、bash/cmd启动脚本及JSON/CSV配置数据可帮助使用者快速搭建从生产者发消息到消费者收消息的完整测试链路并借助聚合报告、响应时间图等监听器分析吞吐量、响应时间与错误率。资源共2881个文件以html说明文档、png架构图示、jar插件库、js/less前端资源及jmx脚本为主体另含properties、xml等配置文件整体约53.02MB目录结构清晰且类型覆盖较全。已有1851人学习下载适合具备一定JMeter基础、希望针对RabbitMQ队列深度、交换机路由及消费并发度做专项压测并据此调优的读者。 我最近在整理压测工具的时候翻出来一个保存很久的压缩包名字叫apache-jmeter-rabbitMQ测试.zip。这玩意儿是我之前给一个订单系统做消息中间件压测时攒下的完整套件里面塞满了 JMeter 的测试计划、RabbitMQ 客户端依赖 jar、消息模板脚本和使用说明。当时为了把这个 zip 从零搭起来踩了不少坑也把 RabbitMQ 的连接管理、交换机路由、消息确认机制这些细节全部过了一遍。今天干脆把这个包打开把里面的东西一条一条讲清楚顺便把 JMeter 压测 RabbitMQ 的完整思路和注意事项都整理出来。不管你是刚接触消息队列还是已经在生产环境用了 RabbitMQ 想验证一下性能上限这篇内容应该都能让你少走一段弯路。1. 先看包里装了啥一个可复用的JMeterRabbitMQ压测套件很多人拿到这种 zip 包第一反应是解压之后直接丢进 JMeter 就跑。但我建议你先花两分钟把目录结构过一遍因为这个包不只是个测试脚本它事实上是一整套压测方案。理解了里面各文件的职责后边改参数、加场景、排问题都会顺手很多。1.1 压缩包内的标准目录结构解压后的目录大概是这样的apache-jmeter-rabbitMQ测试/ ├── jmx/ │ ├── rabbitmq_publish.jmx # 生产者压测计划 │ └── rabbitmq_consume.jmx # 消费者压测计划 ├── lib/ │ ├── amqp-client-5.17.0.jar # RabbitMQ Java客户端 │ ├── jmeter-rabbitmq-sampler.jar # JMeter的RabbitMQ采样器插件 │ ├── slf4j-api-1.7.36.jar │ └── slf4j-simple-1.7.36.jar ├── data/ │ ├── message_template.json # JSON消息体模板 │ └── routing_keys.csv # 路由键参数化数据 └── README.md # 环境说明和使用步骤这个结构是我习惯的业务压测基础布局jmx放测试计划lib放插件和依赖data放参数化数据。插件和依赖单独放出来是因为 JMeter 的类加载机制决定了你光把 jar 塞到 JMeter 的lib/ext可能不够不同版本、不同插件之间还可能有冲突分开管理更清楚。1.2 为什么用JMeter去压RabbitMQ而不是自己写并发程序有朋友问过我一个问题压测 RabbitMQ 这种消息中间件直接用 Java 写个多线程程序不就行了吗为什么还要绕一圈用 JMeter我的回答是你当然可以自己写但 JMeter 的优势在于它已经把线程调度、吞吐量统计、响应时间分布、错误率这些压测基础设施都做完了。你只需要关心“消息怎么发出去”至于并发模型、结果收集、报告生成JMeter 全包了。特别是当你需要在一个团队里做持续的性能回归验证时JMeter 的.jmx测试计划可以进版本库不同人拉下来改几个参数就能复跑比每个人维护一套自研压测代码要省心得多。这个 zip 包里也正是按这个思路组织的生产者计划负责发消息消费者计划负责拉消息两个计划配合起来就能完整模拟出一个业务链路的消息流量。2. 环境准备JDK、JMeter和插件的版本匹配别在最基础的地方翻车这个 zip 包虽然开箱即用但有个前提你的环境得对得上号。我在帮同事搭环境的时候发现大多数问题都不是 RabbitMQ 本身而是 JDK 版本和 JMeter 版本不匹配导致插件加载失败或者控制台直接报ClassNotFoundException。2.1 JDK版本决定你能用哪版JMeterJMeter 5.x 系列的 JDK 要求比较简单粗暴5.4 及以下用 JDK 8 没问题5.6 开始官方建议 JDK 11 以上实际上跑在 JDK 17 上体验最好。RabbitMQ Java 客户端 5.x 则要求 JDK 8 起步这俩通常都能兼容。JMeter版本推荐JDK可用JDK备注5.4.1JDK 8JDK 8/11老项目常用5.6.xJDK 11JDK 8/11/17需注意部分插件对新JDK的兼容5.6.3JDK 17JDK 11/17新装环境建议直接上这个组合我的建议是如果你用的是 JMeter 5.6.3 及以上直接把 JDK 装成 17。如果用的是老测试环境只能跑 JDK 8那就把 JMeter 锁在 5.4.1不要混搭。这个 zip 包里的测试计划是基于 JMeter 5.6.3 保存的低版本打开可能会提示格式不兼容。2.2 插件的两种安装方式RabbitMQ 在 JMeter 的生态里不属于原生支持的协议所以必须有采样器插件来打通。安装方式有两种方式一通过 JMeter 插件管理器安装。把plugins-manager.jar放进lib/ext重启 JMeter打开菜单里的Options - Plugins Manager搜索rabbitmq关键字找到RabbitMQ Sampler勾选安装即可。这种方式的好处是插件依赖会一起处理掉缺点是如果你所在的环境访问不了插件仓库就只能走方式二。方式二手动放置 jar 包。把 Zip 包lib目录下的amqp-client、jmeter-rabbitmq-sampler、slf4j-api这些 jar 全部复制到 JMeter 的lib/ext目录然后重启 JMeter。验证方式很简单右键测试计划里的线程组选择“添加 - 取样器”如果能看到形如RabbitMQ Sampler的选项说明加载成功。如果启动时控制台报了UnsupportedClassVersionError先检查是不是 JDK 版本太低。3. 测试计划怎么写连接复用、消息发送与消费的完整配置这个 zip 里的核心资产其实是两个.jmx测试计划。RabbitMQ Sampler 插件本身配置起来并不难难的是搞清楚每个参数到底影响什么。这里我直接把关键配置拆开来讲同时给出我在实际项目中比较推荐的 JSR223 方案因为它的自由度更高遇到复杂场景更好扩展。3.1 RabbitMQ Sampler 核心参数解析如果你决定使用插件自带的 Sampler打开它的配置界面主要需要填这几块内容Connection 连接信息参数典型值说明Server Host192.168.1.100RabbitMQ 服务端IPServer Port5672AMQP 协议端口Usernameperf_user账号Passwordperf_pass密码Virtual Host/perf虚拟主机隔离业务用Exchange 交换机信息参数典型值说明Exchange Nameperf.exchange交换机名Exchange Typetopicdirect/fanout/topic/headersRouting Keyorder.created路由键Declare Exchangetrue是否自动声明交换机Message 消息属性参数典型值说明Message TypeText Message也支持 Bytes/ObjectDelivery ModePERSISTENT是否持久化Content Typeapplication/json消息类型标识一个容易忽略的点是Declare Exchange和Declare Queue这两个选项。如果设置为true采样器每次发送前都会尝试声明交换机或队列。在压测高并发场景下这相当于多了一次元数据查找开销而且如果交换机已经存在反复声明本身没有意义。我一般建议在测试前先用管理界面或初始化脚本把交换机、队列、绑定关系建好压测时把 Declare 相关选项关掉减少无关操作对性能数据的影响。3.2 生产者压测JSR223 Sampler RabbitMQ Java Client 的组合虽然插件能用但我得说实话在实际压测中我更多用的是 JSR223 Sampler 配合 RabbitMQ Java Client因为这样能完全控制连接的创建时机和消息体构造方式。先看一个最基础的发送脚本import com.rabbitmq.client.ConnectionFactory import com.rabbitmq.client.MessageProperties def factory new ConnectionFactory() factory.setHost(192.168.1.100) factory.setPort(5672) factory.setUsername(perf_user) factory.setPassword(perf_pass) factory.setVirtualHost(/perf) def conn factory.newConnection() def channel conn.createChannel() def payload (order- System.currentTimeMillis()).getBytes(UTF-8) channel.basicPublish(perf.exchange, order.created, MessageProperties.PERSISTENT_TEXT_PLAIN, payload) channel.close() conn.close()这个脚本逻辑上没问题但如果直接放进普通线程组压测会发现 RabbitMQ 服务端的连接数暴涨几百个线程压一会儿管理界面里的 Connections 列表肉眼可见地膨胀。这说明什么问题说明压测工具本身的连接管理开销已经大到影响测试结果了。你原本想测 RabbitMQ 的吞吐上限结果测出的是大量 TCP 握手对服务端的冲击。正确的做法是复用连接、复用 Channel。在你的 JMeter 测试计划里用setUp线程组建立全局连接或者在每个线程第一次运行脚本时初始化 Channel后续迭代只做发送。我这里给出一套比较稳妥的组合方式在setUp线程组里创建一个连接放进全局属性propsimport com.rabbitmq.client.ConnectionFactory import com.rabbitmq.client.Connection def factory new ConnectionFactory() factory.setHost(192.168.1.100) factory.setPort(5672) factory.setUsername(perf_user) factory.setPassword(perf_pass) factory.setVirtualHost(/perf) props.put(rabbitConn, factory.newConnection())然后在普通测试线程组中每个线程第一次迭代时从连接中取 Channel 并缓存到当前线程的vars中import com.rabbitmq.client.Channel def conn props.get(rabbitConn) def channel vars.getObject(channel) if (channel null) { channel conn.createChannel() vars.putObject(channel, channel) } def payload (order- System.currentTimeMillis()).getBytes(UTF-8) channel.basicPublish(perf.exchange, order.created, MessageProperties.PERSISTENT_TEXT_PLAIN, payload)这样整个压测过程只有一个 TCP 连接每个线程持有自己的 Channel既不会把服务端连接数打爆也不会因为多线程共享一个 Channel 而产生线程安全问题。消息发送的 TPS 很快就上去了。3.3 消费者压测主动拉取还是注册消费者消费者场景和生产者不太一样。如果你是做服务端消费能力的验证需要在 JMeter 里模拟一批消费者去队列拉消息常用的是basicGet主动拉取模式import com.rabbitmq.client.Channel def channel vars.getObject(channel) def response channel.basicGet(perf.queue, true) if (response ! null) { def body new String(response.getBody(), UTF-8) vars.put(msgBody, body) SampleResult.setResponseData(body, UTF-8) } else { SampleResult.setResponseMessage(queue empty) }这种模式用起来简单,适合验证队列消费吞吐。但你要注意,它本质上是每次拉一条消息都走一次请求返回,性能上不如basicConsume推送模式。如果压测目标是模拟线上消费者的真实行为,尤其是想验证消息处理成功/失败之后的重试、确认、死信机制,建议直接用basicConsume注册消费者,在回调函数里处理消息。这在 JSR223 里也可以写,只是代码会复杂一些,需要在脚本里维护 Consumer 的生命周期。3.4 用CSV参数化模拟真实业务消息压测不能老发一模一样的消息,否则对路由、磁盘、队列都缺乏说服力。这个 zip 包里放了一个message_template.json和一个routing_keys.csv,就是用来做参数化的。在 JMeter 里添加 CSV Data Set Config,配置好文件名、变量名,然后 JSR223 脚本里通过${routingKey}或者vars.get(routingKey)读取当前迭代的路由键,再替换到 JSON 消息体里。这样做的好处是不需要为每种业务类型单独写脚本,数据驱动即可。4. 压测中真正会踩的坑连接数爆炸、TPS上不去和消息乱码这个部分是我最想分享的。因为配置参数看文档就能学会,但压测过程中出现的各种异常现象,不实际踩一遍很难定位。下面这几个问题,是我在这个 zip 包使用过程中真实遇到过的,每一个都花了不少时间排查。4.1 连接数爆炸看似在测MQ实际在测握手现象很典型:压测跑了不到一分钟,RabbitMQ 管理界面的 Connections 计数已经到了几千,内存居高不下,消息 TPS 反而惨不忍睹。打开 RabbitMQ 日志,满屏都是 AMQP 连接建立的记录。根因在于脚本在每次请求时都执行factory.newConnection()和conn.close()。每一轮迭代都重新完成 TCP 握手、AMQP 协议握手、认证授权,服务端光忙着处理连接生命周期了,哪有资源去路由消息。解决方式就是前面写的连接复用。你可以在压测前把setUp线程组跑一次,建立好连接并放入全局属性;也可以在普通线程组的JSR223 初始化脚本里完成 Channel 的创建。原则只有一条:压测工具自身对被测系统的连接压力,必须被压缩到最低,否则你测出的数据没有任何参考价值。4.2 TPS上不去Channel耗尽和消息持久化的影响有次压测,不管线程组从 50 加到 200,TPS 都死死卡在两千左右。我怀疑是脚本问题,但本地单线程跑发消息很快。后来一排查,发现是测试线程组里的每个线程都在重复创建新的 Connection 和 Channel,而 RabbitMQ 服务端对单连接的 Channel 数有限制,连接数一多,新的 Channel 创建就开始报错,报错后脚本内异常没有被捕获,JMeter 把所有错误请求都算成失败,拉低了整体 TPS。另外,消息持久化也是一个容易忽略的瓶颈。如果你发送的每条消息都设置成PERSISTENT,RabbitMQ 需要把消息落盘,磁盘同步就成了吞吐量的最大约束。压测的时候要分场景:如果线上确实要求持久化,那就用持久化参数去测,并关注磁盘 IO;如果只是想验证路由分发能力,可以先用NON_PERSISTENT跑,把网络和交换机性能底数摸出来,再对比持久化模式下的衰减程度。4.3 消息体乱码和数据格式问题用 JSR223 脚本发中文消息时,一旦服务器环境是 GBK 编码,消费端很容易收到乱码;更隐蔽的是 JSON 消息里某个字段被错误转义,解析失败导致消费端报错。这个问题在 Windows 服务器上尤其常见。处理方式很机械:所有字符串转字节数组时,显式指定字符集。def payload {\orderId\:\${orderId}\,\amount\:${amount}}.getBytes(UTF-8)消费端读取时也统一用 UTF-8 解码。不要依赖平台默认编码,JMeter 本身虽然默认 UTF-8,但 JVM 的系统编码在某些环境下会被改变,一不留神就会踩坑。4.4 消息确认、重试和死信队列的压测场景如果你要对消费失败重试和死信队列做验证,压测脚本就不能简单用basicGet了。你需要手动控制确认行为:消费消息后,如果业务处理成功,调用basicAck;如果处理失败,根据策略决定是basicNack并设置requeuefalse,让消息进入死信队列。这个场景里最容易出的问题是不确认消息导致 unacked 堆积。某个消费者拉取消息后抛了异常,没有执行 ack 也没执行 nack,这条消息就一直挂在那里,超过 prefetch 数量后整个消费者会被阻塞。所以写脚本时必须加try-catch-finally,确保任何分支下消息都有明确的确认或拒绝动作。5. RabbitMQ的适用场景与选型边界什么业务该用它什么业务该换Kafka这个 zip 包压了 RabbitMQ,但很多人拿到手之后可能会想:我的项目到底该不该用 RabbitMQ?网上关于 RabbitMQ、Kafka、RocketMQ 的对比太多了,这里我不重复罗列,只从压测角度给你一个判断框架。5.1 一句话讲清楚什么场景该用RabbitMQRabbitMQ 最擅长的是复杂的路由策略和灵活的消息确认机制。什么叫复杂路由?就是一个消息进来之后,你要根据路由键把它分发到不同的队列,有的队列要求延迟处理,有的队列要求失败后进入死信,有的队列要推送给多个消费者。这些场景下 RabbitMQ 的 direct、topic、fanout、headers 四种交换机和配套的绑定规则,能让你很优雅地实现业务逻辑。典型例子:订单超时未支付自动关闭。你发一条延迟消息,到时间点了 RabbitMQ 自动把它投递到关闭订单的队列;如果关闭失败,消息进入死信队列,等待补偿任务处理。这种按业务语义灵活路由的能力,是 Kafka 这类偏日志流式系统不太擅长的。5.2 和Kafka、RocketMQ的压测关注点差异从压测视角看,三种中间件测的东西完全不同:维度RabbitMQKafkaRocketMQ典型吞吐量万级百万级十万级路由能力非常灵活弱,偏顺序读写中等消息确认完善的 ack/nack/requeue偏移量提交事务消息、顺序消息压测重点路由规则、确认机制、死信分区吞吐、消费者组均衡事务消息、消息顺序常见故障连接数、Channel数、未确认堆积分区不均衡、消费者Lag事务状态回查、顺序错乱如果你用这个 zip 包里的思路去压测 Kafka,那就不能用basicPublish这类 AMQP 语义了,而是要换 Kafka 的生产者 API 和消费者 API,关注点也变成分区数、批量大小、acks 参数。所以选型的时候要想清楚:你需要的到底是灵活路由和可靠确认,还是海量吞吐和流式处理。选错了中间件,后续的压测方案、优化手段全都不在一个方向上。5.3 什么时候果断放弃RabbitMQ当你的业务场景是海量日志采集、用户行为埋点、需要几十万的吞吐持续灌数据时,RabbitMQ 就不太合适了。这种场景下 Kafka 的批量写入和顺序读盘特性,才是正确选择。另外,如果团队已经重度使用某个云厂商的消息队列产品,比如 RocketMQ,那么兼容性、运维成本、周边生态都得综合考量,而不是只盯着单机性能。压测的价值就在于此:用数据告诉你中间件的上限在哪,瓶颈在哪,以及它到底适不适合你的业务模型。这个 zip 包给你的是一个起点,你把它跑通、看懂每一项指标的含义之后,完全可以按自己的业务场景去改造脚本。最后再分享一点我个人的操作习惯。每次拿到这种压测 zip 包,我不会急着开压,而是先把 RabbitMQ 管理界面打开,确认三件事:交换机、队列、绑定关系是否都已经存在并符合预期。很多压测结果没法看,不是因为 JMeter 脚本写错了,而是消息发到了一个没有消费者监听的队列里,看着 TPS 很高,实际上一堆消息全部积压。先看清楚队列的健康状态,再动手压,你会发现排查问题的时间至少省一半。本文还有配套的精品资源点击获取