前言
最近在项目中引入 RocketMQ 作为消息中间件,选择使用 Docker 容器化部署以简化环境搭建。本以为按照官方文档几条命令就能搞定,结果连续踩了两个大坑:先是生产者发送消息超时,修复后 Console 管理界面又连不上 Broker。
如果你也在用 Docker 部署 RocketMQ,或者正准备搭建,希望这篇实录能帮你避开我踩过的坑。
读完本文,你将掌握:
- RocketMQ 5.x Local 模式的正确部署方式
- 生产者超时问题的根本原因与解决方案
- Console 无法连接 Broker 的终极修复方法
- 一套完整可用的 Docker Compose 配置
一、环境准备
1.1 创建独立网络
RocketMQ 的 NameServer、Broker、Console 三个组件需要网络互通,创建一个独立的 Docker 网络是最佳实践:
bash
# 创建 rocketmq 网络
docker network create rocketmq
# 验证网络创建成功
docker inspect rocketmq
为什么需要独立网络?
- 容器间可以通过容器名直接通信(如
mqnamesrv:9876) - 与宿主机网络隔离,安全性更高
- 便于统一管理网络策略
1.2 拉取官方镜像
bash
docker pull apache/rocketmq:5.1.0
本文使用 RocketMQ 5.1.0 版本,这是目前较新的稳定版本。5.x 引入了 Proxy 代理层,架构与 4.x 有较大变化,后续会详细说明。
1.3 目录规划
bash
# NameServer 日志目录
mkdir -p /usr/local/rocketmq/nameserver/logs
chmod 777 -R /usr/local/rocketmq/nameserver/*
# Broker 日志和配置目录
mkdir -p /usr/local/rocketmq/broker/logs
mkdir -p /usr/local/rocketmq/broker/conf
chmod 777 -R /usr/local/rocketmq/broker/*
提示 :使用
chmod 777是为了让容器内的 rocketmq 用户有写入权限,生产环境建议通过用户组管理权限。
二、标准部署流程
2.1 部署 NameServer
NameServer 是什么?
NameServer 是 RocketMQ 的轻量级路由注册中心,类似于 Dubbo 的 Zookeeper,但更简单:
- 无状态:节点之间不通信
- Broker 管理:接收 Broker 注册,进行心跳检测
- 路由管理:为 Producer 和 Consumer 提供路由信息
bash
docker run -d --name mqnamesrv -p 9876:9876 --network rocketmq \
-v /usr/local/rocketmq/nameserver/logs:/home/rocketmq/logs \
-e "MAX_HEAP_SIZE=256M" \
-e "HEAP_NEWSIZE=128M" \
apache/rocketmq:5.1.0 sh mqnamesrv
验证启动:
bash
docker logs mqnamesrv
看到 The Name Server boot success 字样即表示启动成功。
2.2 部署 Broker
Broker 与 Proxy 的关系
RocketMQ 5.x 引入了 Proxy 层,位于客户端和 Broker 之间:
- 4.x 架构:客户端 → Broker
- 5.x 架构:客户端 → Proxy → Broker
Proxy 对外屏蔽了 NameServer、Broker 的概念,统一提供消息服务接口,同时支持多种协议(gRPC、HTTP 等)。
官方推荐使用 Local 模式部署,即 Broker 和 Proxy 同进程运行,减少网络开销。
bash
docker run -d --name mqbroker -p 10911:10911 -p 10909:10909 --network rocketmq \
-v /usr/local/rocketmq/broker/logs:/root/logs \
-e "MAX_HEAP_SIZE=512M" \
-e "HEAP_NEWSIZE=256M" \
apache/rocketmq:5.1.0 sh mqbroker -n mqnamesrv:9876 --enable-proxy \
autoCreateTopicEnable=true \
-c /home/rocketmq/rocketmq-5.1.0/conf/broker.conf
端口说明:
10911:Broker 对外服务端口10909:Broker HA(高可用)端口
验证启动:
bash
docker exec -it mqbroker bash -c "tail -n 10 /home/rocketmq/logs/rocketmqlogs/proxy.log"
2.3 部署 Console 管理界面
RocketMQ Console 是官方推荐的第三方管理工具,提供图形化界面用于监控消息堆积、消费者状态等。
bash
docker run -d --name mqconsole -p 8098:8080 --network rocketmq \
-e "JAVA_OPTS=-Xmx256M -Xms256M -Xmn128M \
-Drocketmq.namesrv.addr=mqnamesrv:9876 \
-Dcom.rocketmq.sendMessageWithVIPChannel=false" \
styletang/rocketmq-console-ng
访问 http://<宿主机IP>:8098 即可看到控制台。
三、第一个坑:生产者发送超时
3.1 问题现象
按照上述步骤部署后,Console 能正常连接,一切看起来很美好。但在 Spring Boot 应用中发送消息时:
java
rocketMQTemplate.syncSend("hotel-booking-test-topic:TEST_TAG", "Hello RocketMQ");
报错信息:
org.apache.rocketmq.remoting.exception.RemotingTooMuchRequestException: sendDefaultImpl call timeout
at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendDefaultImpl
3.2 根因分析
通过排查日志和网络,发现问题出在 Broker IP 注册上:
问题链路:
- Broker 启动时,向 NameServer 注册自己的地址
- 默认情况下 ,Broker 使用容器的内部 IP(如
172.18.0.3)注册 - 当外部的生产者向 NameServer 查询路由时,得到的是容器 IP
- 生产者尝试连接
172.18.0.3:10911,但这个 IP 在宿主机网络中不可达 - 连接超时!
网络拓扑示意:
┌─────────────────┐
│ 生产者(宿主机) │
└────────┬────────┘
│ 查询路由
↓
┌─────────────────┐
│ NameServer │ → 返回 brokerIP=172.18.0.3
└─────────────────┘
│
│ 尝试连接 172.18.0.3:10911 ❌ (不可达)
↓
┌─────────────────┐
│ Broker 容器 │ 实际可访问: 宿主机IP:10911
│ IP: 172.18.0.3 │
└─────────────────┘
3.3 解决方案:配置 brokerIP1
让 Broker 向 NameServer 注册时,使用宿主机 IP 而非容器 IP。
步骤一:创建配置文件
bash
# 在宿主机创建配置文件
cat > /usr/local/rocketmq/broker/conf/broker.conf << 'EOF'
brokerIP1=你的宿主机IP
autoCreateTopicEnable=true
EOF
重要 :
brokerIP1必须配置为宿主机的实际 IP,不能是127.0.0.1或容器 IP。
步骤二:重新部署 Broker(挂载配置文件)
bash
# 停止并删除旧容器
docker stop mqbroker && docker rm mqbroker
# 重新运行,挂载配置文件
docker run -d --name mqbroker \
-p 10911:10911 -p 10909:10909 \
--network rocketmq \
-v /usr/local/rocketmq/broker/logs:/root/logs \
-v /usr/local/rocketmq/broker/conf/broker.conf:/home/rocketmq/rocketmq-5.1.0/conf/broker.conf \
-e "MAX_HEAP_SIZE=512M" \
-e "HEAP_NEWSIZE=256M" \
apache/rocketmq:5.1.0 sh mqbroker -n mqnamesrv:9876 \
-c /home/rocketmq/rocketmq-5.1.0/conf/broker.conf
验证修复:
bash
# 查看 Console 中的 Broker 信息
# 应该看到 brokerIP1 已经显示为宿主机 IP
此时,生产者应该可以正常发送消息了!
四、第二个坑:Console 连不上 Broker
4.1 问题现象
修复了生产者超时问题后,你以为一切正常了,结果打开 Console...
Console 状态:
- NameServer 连接正常 ✓
- Broker 列表为空 ✗
- 无法查看 Topic 和消息
Console 日志:
Error connecting to Broker: Connection refused
4.2 根因分析
这是一个典型的拆东墙补西墙问题:
配置 brokerIP1 前:
- Console 在容器网络内,可以通过容器名
mqbroker:10911访问 Broker ✓ - 外部生产者无法访问 Broker ✗
配置 brokerIP1 后:
- 外部生产者通过宿主机 IP 访问 Broker ✓
- Console 仍然尝试通过容器网络访问
mqbroker:10911,但 Broker 向 NameServer 注册的地址是宿主机 IP ✗
网络冲突示意:
┌─────────────────┐
│ Console 容器 │ → 访问 mqbroker:10911 (容器网络)
└─────────────────┘
│
│ NameServer 返回: 宿主机IP:10911
↓
┌─────────────────┐
│ Broker 注册地址 │ = 宿主机IP:10911
└─────────────────┘
│
│ Console 尝试连接 宿主机IP:10911
↓
┌─────────────────┐
│ 网络路由问题 │ 容器内访问宿主机IP需要特殊配置
└─────────────────┘
4.3 终极解决方案:Console 使用 host 网络模式
让 Console 容器直接使用宿主机的网络栈,这样它可以通过 127.0.0.1:10911 访问 Broker(端口已映射到宿主机)。
bash
# 停止并删除旧 Console
docker stop mqconsole && docker rm mqconsole
# 使用 host 网络模式重新部署
docker run -d --name mqconsole --network host \
-e "JAVA_OPTS=-Xmx256M -Xms256M -Xmn128M \
-Drocketmq.namesrv.addr=127.0.0.1:9876 \
-Dcom.rocketmq.sendMessageWithVIPChannel=false" \
styletang/rocketmq-console-ng
为什么这样能解决问题?
使用 --network host 后:
- Console 容器不再有独立的网络命名空间
- 直接使用宿主机的 IP 和端口
- 访问
127.0.0.1:9876和127.0.0.1:10911等同于访问宿主机本地服务 - NameServer 返回的 Broker 地址(宿主机 IP)可以直接访问
访问 Console:
bash
# 不再需要端口映射,直接访问宿主机端口
# 假设 Console 默认端口是 8080
http://<宿主机IP>:8080
4.4 备用方案:本地路由转发(不推荐)
如果不使用 host 网络模式,也可以配置宿主机的路由规则,但配置较复杂:
bash
# 启用本地路由转发
sysctl -w net.ipv4.conf.all.route_localnet=1
# 放通防火墙规则
iptables -I INPUT -d <宿主机IP> -p tcp --dport 10911 -j ACCEPT
建议:优先使用 host 网络模式,简单且不易出错。
五、完整可运行配置
5.1 一键部署脚本
将以下脚本保存为 deploy-rocketmq.sh,修改 BROKER_IP 为你的宿主机 IP 后执行:
bash
#!/bin/bash
# ========== 配置区 ==========
BROKER_IP="192.168.1.100" # 修改为你的宿主机 IP
# ===========================
# 1. 创建网络
docker network create rocketmq 2>/dev/null || echo "网络已存在"
# 2. 创建目录
mkdir -p /usr/local/rocketmq/nameserver/logs
mkdir -p /usr/local/rocketmq/broker/logs
mkdir -p /usr/local/rocketmq/broker/conf
chmod 777 -R /usr/local/rocketmq/*
# 3. 创建 Broker 配置文件
cat > /usr/local/rocketmq/broker/conf/broker.conf << EOF
brokerIP1=$BROKER_IP
autoCreateTopicEnable=true
namesrvAddr=mqnamesrv:9876
EOF
# 4. 启动 NameServer
docker run -d --name mqnamesrv -p 9876:9876 --network rocketmq \
-v /usr/local/rocketmq/nameserver/logs:/home/rocketmq/logs \
-e "MAX_HEAP_SIZE=256M" \
-e "HEAP_NEWSIZE=128M" \
apache/rocketmq:5.1.0 sh mqnamesrv
# 等待 NameServer 启动
sleep 5
# 5. 启动 Broker
docker run -d --name mqbroker \
-p 10911:10911 -p 10909:10909 \
--network rocketmq \
-v /usr/local/rocketmq/broker/logs:/root/logs \
-v /usr/local/rocketmq/broker/conf/broker.conf:/home/rocketmq/rocketmq-5.1.0/conf/broker.conf \
-e "MAX_HEAP_SIZE=512M" \
-e "HEAP_NEWSIZE=256M" \
apache/rocketmq:5.1.0 sh mqbroker -n mqnamesrv:9876 \
-c /home/rocketmq/rocketmq-5.1.0/conf/broker.conf
# 等待 Broker 启动
sleep 10
# 6. 启动 Console(使用 host 网络)
docker run -d --name mqconsole --network host \
-e "JAVA_OPTS=-Xmx256M -Xms256M -Xmn128M \
-Drocketmq.namesrv.addr=127.0.0.1:9876 \
-Dcom.rocketmq.sendMessageWithVIPChannel=false" \
styletang/rocketmq-console-ng
echo "========== 部署完成 =========="
echo "NameServer: localhost:9876"
echo "Broker: $BROKER_IP:10911"
echo "Console: http://localhost:8080"
echo "============================"
5.2 Spring Boot 配置示例
yaml
# application.yml
rocketmq:
name-server: localhost:9876
producer:
group: hotel-booking-producer-group
send-message-timeout: 3000
retry-times-when-send-failed: 2
java
// 发送消息示例
@Service
public class OrderService {
@Autowired
private RocketMQTemplate rocketMQTemplate;
public void sendOrderMessage(Order order) {
rocketMQTemplate.syncSend(
"hotel-booking-test-topic:TEST_TAG",
order,
3000 // 超时时间 3 秒
);
}
}
5.3 验证命令清单
bash
# 检查容器状态
docker ps -a | grep mq
# 查看 NameServer 日志
docker logs mqnamesrv
# 查看 Broker 日志
docker logs mqbroker
# 测试生产者连接
telnet <宿主机IP> 10911
# 查看 Console 中的 Broker 状态
# 访问 http://<宿主机IP>:8080,检查 "Broker" 菜单
六、最佳实践与避坑指南
6.1 生产环境建议
| 组件 | 开发/测试环境 | 生产环境 |
|---|---|---|
| 部署方式 | Docker 单机 | Docker Swarm / K8s 集群 |
| NameServer | 1 个节点 | ≥3 个节点(高可用) |
| Broker | 单 Master | Master-Slave 组成集群 |
| 存储映射 | 宿主机目录 | 分布式存储 / PVC |
| JVM 内存 | 256M~512M | ≥4G(根据消息量调整) |
| 监控 | Console | Prometheus + Grafana |
6.2 常见错误对照表
| 错误信息 | 可能原因 | 解决方案 |
|---|---|---|
sendDefaultImpl call timeout |
brokerIP1 配置错误 | 配置为宿主机 IP |
Connection refused |
端口未映射或防火墙拦截 | 检查 -p 参数和防火墙规则 |
| Console 无法连接 Broker | 网络模式冲突 | Console 使用 --network host |
No route info of this topic |
Topic 不存在 | 启用 autoCreateTopicEnable=true |
| Broker 启动失败 | 配置文件路径错误 | 检查 -c 参数和挂载路径 |
6.3 Docker Compose 版本(推荐)
如果使用 Docker Compose,配置更简洁:
yaml
version: '3.8'
services:
namesrv:
image: apache/rocketmq:5.1.0
container_name: mqnamesrv
ports:
- "9876:9876"
environment:
- MAX_HEAP_SIZE=256M
- HEAP_NEWSIZE=128M
networks:
- rocketmq
command: sh mqnamesrv
volumes:
- ./data/namesrv/logs:/home/rocketmq/logs
broker:
image: apache/rocketmq:5.1.0
container_name: mqbroker
ports:
- "10911:10911"
- "10909:10909"
environment:
- MAX_HEAP_SIZE=512M
- HEAP_NEWSIZE=256M
networks:
- rocketmq
command: sh mqbroker -n mqnamesrv:9876 -c /home/rocketmq/rocketmq-5.1.0/conf/broker.conf
volumes:
- ./data/broker/logs:/root/logs
- ./data/broker/conf/broker.conf:/home/rocketmq/rocketmq-5.1.0/conf/broker.conf
depends_on:
- namesrv
console:
image: styletang/rocketmq-console-ng
container_name: mqconsole
network_mode: host
environment:
- JAVA_OPTS=-Xmx256M -Xms256M -Xmn128M -Drocketmq.namesrv.addr=127.0.0.1:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false
depends_on:
- namesrv
- broker
networks:
rocketmq:
driver: bridge
启动:
bash
# 确保 broker.conf 中的 brokerIP1 已配置
docker-compose up -d
总结
通过本文的实战记录,我们解决了 Docker 部署 RocketMQ 时的两个核心问题:
- 生产者超时 :通过配置
brokerIP1让 Broker 注册宿主机 IP - Console 连不上 :通过
--network host让 Console 直接访问宿主机网络