RocketMQ 生产部署

📖 前置阅读:本文是 RocketMQ 系列的终篇,假设读者已经掌握前五篇的全部内容(核心架构、SpringBoot 集成、高级消息类型、可靠性、消费者模式)。

一、⚡ 问题切入:单机 Docker 的瓶颈

第一篇搭的单机 RocketMQ(一个 NameServer + 一个 Broker)只能用来学习——生产环境中:

单点后果
一台 NameServer 挂了Producer/Consumer 无法获取路由——整个 MQ 瘫痪
一台 Broker 挂了所有消息不可用——消息无法发送/消费
JVM 内存不足Full GC 频繁 → 消息延迟抖动 → 超时重试雪崩
磁盘写满CommitLog 无法写入 → 生产者阻塞

生产最低配:2 台 NameServer + 至少 2 台 Broker(主从)。

二、双主双从高可用集群搭建

2.1 架构设计

NameServer 集群:2 台(互不通信,各自独立)
Broker 集群:2 组主从(Master-A + Slave-A、Master-B + Slave-B)

            NameServer-1          NameServer-2
           (192.168.1.10:9876)   (192.168.1.11:9876)
                  ↑                       ↑
        ┌─────────┴───────┬───────────────┘
        │                 │
   Broker-A (Master)  Broker-A (Slave)
   192.168.1.20:10911  192.168.1.21:10911
   brokerId=0           brokerId=1

   Broker-B (Master)  Broker-B (Slave)
   192.168.1.22:10911  192.168.1.23:10911
   brokerId=0           brokerId=1

路由发现:Producer/Consumer 配置所有的 NameServer 地址——只要有一台 NameServer 活着,路由就能工作。

2.2 Docker Compose 双主双从

# docker-compose-cluster.yml
version: '3.8'
services:
  # ========== NameServer 集群 ==========
  namesrv1:
    image: apache/rocketmq:5.1.4
    container_name: rocketmq-namesrv1
    command: sh mqnamesrv
    ports:
      - "9876:9876"
    environment:
      - JAVA_OPT_EXT=-Xms512m -Xmx512m

  namesrv2:
    image: apache/rocketmq:5.1.4
    container_name: rocketmq-namesrv2
    command: sh mqnamesrv
    ports:
      - "9877:9876"
    environment:
      - JAVA_OPT_EXT=-Xms512m -Xmx512m

  # ========== Broker-A Master ==========
  broker-a-m:
    image: apache/rocketmq:5.1.4
    container_name: rocketmq-broker-a-m
    command: sh mqbroker -c /home/rocketmq/conf/broker-a-m.conf
    ports:
      - "11911:11911"   # remoting
      - "11909:11909"   # VIP channel
    volumes:
      - ./conf/broker-a-m.conf:/home/rocketmq/rocketmq-5.1.4/conf/broker-a-m.conf
      - ./data/broker-a-m:/home/rocketmq/store
    environment:
      - JAVA_OPT_EXT=-Xms2g -Xmx2g

  # ========== Broker-A Slave ==========
  broker-a-s:
    image: apache/rocketmq:5.1.4
    container_name: rocketmq-broker-a-s
    command: sh mqbroker -c /home/rocketmq/conf/broker-a-s.conf
    ports:
      - "12911:12911"
      - "12909:12909"
    volumes:
      - ./conf/broker-a-s.conf:/home/rocketmq/rocketmq-5.1.4/conf/broker-a-s.conf
      - ./data/broker-a-s:/home/rocketmq/store
    environment:
      - JAVA_OPT_EXT=-Xms2g -Xmx2g

  # ========== Dashboard ==========
  dashboard:
    image: apacherocketmq/rocketmq-dashboard:1.0.1
    container_name: rocketmq-dashboard
    ports:
      - "8080:8080"
    environment:
      - JAVA_OPTS=-Drocketmq.namesrv.addr=namesrv1:9876;namesrv2:9876

关键配置文件:

# conf/broker-a-m.conf —— Master-A
brokerClusterName = DefaultCluster
brokerName = broker-a           # Broker组名——同一组的Master和Slave用同一个名字
brokerId = 0                    # 0=Master, 非0=Slave
brokerRole = SYNC_MASTER        # 同步主从
flushDiskType = ASYNC_FLUSH     # 异步刷盘(可靠性交给主从同步)
namesrvAddr = namesrv1:9876;namesrv2:9876   # 两个 NameServer
listenPort = 11911
storePathRootDir = /home/rocketmq/store
storePathCommitLog = /home/rocketmq/store/commitlog
autoCreateTopicEnable = false   # 生产环境关闭——Topic 必须手动创建
# conf/broker-a-s.conf —— Slave-A
brokerClusterName = DefaultCluster
brokerName = broker-a           # 和 Master 同一个 brokerName
brokerId = 1                    # 非 0 表示 Slave
brokerRole = SLAVE
flushDiskType = ASYNC_FLUSH
namesrvAddr = namesrv1:9876;namesrv2:9876
listenPort = 12911
storePathRootDir = /home/rocketmq/store

⚠️ 新手提示:Master 和 Slave 必须使用相同的 brokerName——这是 RocketMQ 识别它们属于同一组主从的唯一方式。Master 的 brokerId=0,Slave 的 brokerId 为任意非 0 整数。

2.3 手动创建 Topic

生产环境建议关闭 autoCreateTopicEnable,手动创建 Topic:

# 进入 Broker 容器创建 Topic
docker exec -it rocketmq-broker-a-m sh mqadmin updateTopic \
  -n namesrv1:9876 \
  -t order-topic \          # Topic 名称
  -c DefaultCluster \       # 集群名
  -w 8 \                    # 写 Queue 数
  -r 8                      # 读 Queue 数(通常和写一致)

# 验证
docker exec -it rocketmq-broker-a-m sh mqadmin topicList \
  -n namesrv1:9876

三、Dashboard 监控

RocketMQ Dashboard 是一个 Web 管理界面,功能比 RabbitMQ 管理界面更丰富:

访问 http://192.168.1.20:8080

核心页面

Tab看什么为什么要看
ClusterBroker 列表、主从关系、同步状态Broker 是否存活、主从同步是否正常
Topic每个 Topic 的消息量、TPS、Queue 分布哪些 Topic 流量异常
Consumer消费者组列表、消费 TPS、积压量 (Diff)最关键的指标——Diff 持续增长 = 消费跟不上生产
Message按 msgId/key/Topic 查询消息内容排查"这条消息去哪了"
Message Trace消息的生产→存储→消费全链路轨迹排查延迟瓶颈在哪个环节

必须盯住的三根线

指标Dashboard 看哪里告警阈值
消费积压 (Diff)Consumer → Diff 列Diff > Topic 日均消息量的 2 倍
Broker TPSTopic → 各 Broker 的 TPS接近 Broker 单机上限(约 5 万/s)
磁盘使用Cluster → Broker 详情 → 磁盘使用> 80%

四、性能调优

4.1 JVM 调优

RocketMQ Broker 是 Java 进程——GC 停顿直接影响消息延迟:

# Broker 的 JVM 参数(docker-compose 中 environment 段)
JAVA_OPT_EXT=-Xms4g -Xmx4g \
  -XX:+UseG1GC \                           # 用 G1 GC(低延迟)
  -XX:G1HeapRegionSize=16m \               # G1 region 大小
  -XX:MaxGCPauseMillis=200 \               # 目标 GC 停顿 < 200ms
  -XX:InitiatingHeapOccupancyPercent=45 \  # 堆使用 45% 开始并发标记
  -XX:+PrintGCDetails \
  -XX:+PrintGCDateStamps

4.2 OS 调优

# 虚拟内存——防止 CommitLog 写入时 OOM
sysctl -w vm.min_free_kbytes=1048576

# 最大文件句柄数——CommitLog 和 ConsumeQueue 需要大量文件描述符
ulimit -n 65536

4.3 应用层——SpringBoot 生产者调优

rocketmq:
  name-server: namesrv1:9876;namesrv2:9876
  producer:
    group: order-producer-group
    # 发送超时(ms)——同步发送时最关键
    send-message-timeout: 5000
    # 重试次数——生产端重试,不是消费端
    retry-times-when-send-failed: 3
    retry-times-when-send-async-failed: 3
    # 客户端线程池大小
    compress-message-body-threshold: 4096
    # 消息体超过此大小自动压缩
    max-message-size: 4194304

4.4 应用层——SpringBoot 消费者调优

@RocketMQMessageListener(
    topic = "order-topic",
    consumerGroup = "order-consumer-group",
    consumeThreadNumber = 30,           // 消费线程数——不是越大越好
    consumeMessageBatchMaxSize = 32,    // 每次拉取最多 32 条
    maxReconsumeTimes = 5              // 最大重试次数——不要总用默认 16 次
)
参数调大调小默认值
consumeThreadNumber计算密集型消费I/O 密集型消费(如调外部 API)20
consumeMessageBatchMaxSize消息体小,批处理收益大消息体大或消费耗时不可控32
maxReconsumeTimes快速失败而不是长时间重试16

⚠️ 新手提示:consumeThreadNumber 不要设成几百——消费线程是需要 CPU 时间片的。RocketMQ 建议最多 64 个线程。实际经验:20 ~ 40 足够覆盖绝大多数场景。

五、常见生产故障

故障现象排查
消费积压(Lag)Dashboard Consumer Diff 持续增长① 消费线程数是否够 ② 消费逻辑是否有慢调用 ③ 增加 Queue 数 ④ 增加消费者实例
消息延迟抖动生产 TPS 周期性下降① Broker GC 日志检查 ② 是否到磁盘写入瓶颈 ③ vmstat 1 看 IO 等待
No route info生产者发送报错 “No route info of this topic”① Topic 是否手动创建了 ② Broker 的 autoCreateTopicEnable=true 是否已开 ③ NameServer 地址配全了吗
Rebalance 风暴消费者日志频繁 Rebalance① 消费者实例是否频繁重启 ② 网络是否稳定(心跳 30s,超时 2min)③ clientCallbackExecutorThreads 是否太小
消费失败循环同一条消息反复重试① 代码 Bug——同一输入不可能重试成功 ② maxReconsumeTimes 设小一些 ③ 检查为啥不满足进死信的条件
磁盘写满Broker 日志 “disk full”fileReservedTime 调小(默认 72h)② 扩大磁盘 ③ 监控盘使用率

六、上线前 10 项检查清单

#检查项配置/命令
1NameServer 至少 2 台Docker Compose 中起两个实例
2关键 Topic 的 Broker 配主从brokerRole=SYNC_MASTER
3autoCreateTopicEnable=falseTopic 必须手动创建——防止业务代码写错 Topic 名
4关键业务的 Queue 数 ≥ 预期最大消费者实例数创建时一步到位——Queue 只增不减
5消费者 maxReconsumeTimes 按业务设(不是默认 16)不重要的业务 3~5 次够了
6配好死信消费者监听 %DLQ%{consumerGroup},进死信就告警
7NameServer 地址用分号分隔配全namesrv1:9876;namesrv2:9876
8接入 Prometheus + Grafana 或 Dashboard最少盯住消费积压 (Diff)
9Broker JVM 堆内存 ≥ 2G-Xms2g -Xmx2g
10Broker 磁盘使用率告警 < 80%Monitor 脚本定时检查

七、RocketMQ vs RabbitMQ 最终选型

六篇学完了 RabbitMQ,六篇学完了 RocketMQ。实际选型时:

场景选谁理由
有事务消息需求(下单+扣库存+通知)RocketMQRabbitMQ 没有原生实现
需要海量吞吐(> 10万 msg/s)RocketMQCommitLog 顺序写碾压随机写
路由逻辑极度灵活(一个消息按多种规则分发给不同消费者)RabbitMQExchange + Binding 模型比 Topic+Tag 灵活
小团队,运维简单RabbitMQ单 Docker 即可,管理界面直观
Java 技术栈,需要深度定制RocketMQ全部 Java 实现,二次开发方便
云厂商托管阿里云 → RocketMQ;AWS → RabbitMQ云厂商决定了用什么

🎯 总结

RocketMQ 的生产部署核心在三点:

  1. 高可用架构:至少 2 个 NameServer(互不通信)+ 至少 1 组主从 Broker(SYNC_MASTER)。NameServer 地址用分号分隔全配——只要有一台活着路由就能工作。

  2. Dashboard 监控:消费积压 (Diff) 是最关键的指标——持续增长说明消费跟不上生产。Dashboard 的 Message Trace 可以追踪一条消息的完整生命周期。

  3. 调优常识:Broker JVM 用 G1GC + 堆 ≥ 2G,OS 调大文件句柄,消费线程 20 ~ 40 足够,maxReconsumeTimes 不超过业务容忍上限。


📖 系列总览

RocketMQ 六篇系列到此结束:

#核心收获
1核心架构与消息模型NameServer 去中心化路由、CommitLog 顺序写、Topic+Queue+Tag 三级分类
2SpringBoot 全操作指南RocketMQTemplate 三模式发送、@RocketMQMessageListener 消费、订单消息实战
3顺序/延迟/事务消息18 级延迟、半消息 + 回查事务消息、顺序消费的挂起重试
4消息可靠性与容错刷盘策略、主从同步、16 次递增重试、%DLQ% 死信、幂等
5消费者模式与过滤器集群/广播、Push(长轮询Pull)、Tag/SQL92 过滤、Rebalance
6生产环境部署与调优双主双从集群、Dashboard 监控、JVM/OS 调优、10 项检查清单

建议从 1 到 6 顺序阅读,每篇以前一篇为前提。学完这六篇,从基本概念到生产部署的全链路都覆盖了。