Docker 部署 RocketMQ 5.1.0 踩坑实录:从超时到 Console 连不上的完整解决之路

0 阅读9分钟

前言

最近在项目中引入 RocketMQ 作为消息中间件,选择使用 Docker 容器化部署以简化环境搭建。本以为按照官方文档几条命令就能搞定,结果连续踩了两个大坑:先是生产者发送消息超时,修复后 Console 管理界面又连不上 Broker。

如果你也在用 Docker 部署 RocketMQ,或者正准备搭建,希望这篇实录能帮你避开我踩过的坑。

读完本文,你将掌握:

  • RocketMQ 5.x Local 模式的正确部署方式
  • 生产者超时问题的根本原因与解决方案
  • Console 无法连接 Broker 的终极修复方法
  • 一套完整可用的 Docker Compose 配置

一、环境准备

1.1 创建独立网络

RocketMQ 的 NameServer、Broker、Console 三个组件需要网络互通,创建一个独立的 Docker 网络是最佳实践:

# 创建 rocketmq 网络
docker network create rocketmq

# 验证网络创建成功
docker inspect rocketmq

为什么需要独立网络?

  • 容器间可以通过容器名直接通信(如 mqnamesrv:9876
  • 与宿主机网络隔离,安全性更高
  • 便于统一管理网络策略

1.2 拉取官方镜像

docker pull apache/rocketmq:5.1.0

本文使用 RocketMQ 5.1.0 版本,这是目前较新的稳定版本。5.x 引入了 Proxy 代理层,架构与 4.x 有较大变化,后续会详细说明。

1.3 目录规划

# 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 提供路由信息
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

验证启动:

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 同进程运行,减少网络开销。

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(高可用)端口

验证启动:

docker exec -it mqbroker bash -c "tail -n 10 /home/rocketmq/logs/rocketmqlogs/proxy.log"

2.3 部署 Console 管理界面

RocketMQ Console 是官方推荐的第三方管理工具,提供图形化界面用于监控消息堆积、消费者状态等。

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 应用中发送消息时:

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 注册上:

问题链路:

  1. Broker 启动时,向 NameServer 注册自己的地址
  2. 默认情况下,Broker 使用容器的内部 IP(如 172.18.0.3)注册
  3. 当外部的生产者向 NameServer 查询路由时,得到的是容器 IP
  4. 生产者尝试连接 172.18.0.3:10911,但这个 IP 在宿主机网络中不可达
  5. 连接超时!

网络拓扑示意:

┌─────────────────┐
│  生产者(宿主机)  │
└────────┬────────┘
         │ 查询路由
         ↓
┌─────────────────┐
│  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。

步骤一:创建配置文件

# 在宿主机创建配置文件
cat > /usr/local/rocketmq/broker/conf/broker.conf << 'EOF'
brokerIP1=你的宿主机IP
autoCreateTopicEnable=true
EOF

重要brokerIP1 必须配置为宿主机的实际 IP,不能是 127.0.0.1 或容器 IP。

步骤二:重新部署 Broker(挂载配置文件)

# 停止并删除旧容器
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

验证修复:

# 查看 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(端口已映射到宿主机)。

# 停止并删除旧 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:9876127.0.0.1:10911 等同于访问宿主机本地服务
  • NameServer 返回的 Broker 地址(宿主机 IP)可以直接访问

访问 Console:

# 不再需要端口映射,直接访问宿主机端口
# 假设 Console 默认端口是 8080
http://<宿主机IP>:8080

4.4 备用方案:本地路由转发(不推荐)

如果不使用 host 网络模式,也可以配置宿主机的路由规则,但配置较复杂:

# 启用本地路由转发
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 后执行:

#!/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 配置示例

# application.yml
rocketmq:
  name-server: localhost:9876
  producer:
    group: hotel-booking-producer-group
    send-message-timeout: 3000
    retry-times-when-send-failed: 2
// 发送消息示例
@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 验证命令清单

# 检查容器状态
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 集群
NameServer1 个节点≥3 个节点(高可用)
Broker单 MasterMaster-Slave 组成集群
存储映射宿主机目录分布式存储 / PVC
JVM 内存256M~512M≥4G(根据消息量调整)
监控ConsolePrometheus + Grafana

6.2 常见错误对照表

错误信息可能原因解决方案
sendDefaultImpl call timeoutbrokerIP1 配置错误配置为宿主机 IP
Connection refused端口未映射或防火墙拦截检查 -p 参数和防火墙规则
Console 无法连接 Broker网络模式冲突Console 使用 --network host
No route info of this topicTopic 不存在启用 autoCreateTopicEnable=true
Broker 启动失败配置文件路径错误检查 -c 参数和挂载路径

6.3 Docker Compose 版本(推荐)

如果使用 Docker Compose,配置更简洁:

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

启动:

# 确保 broker.conf 中的 brokerIP1 已配置
docker-compose up -d

总结

通过本文的实战记录,我们解决了 Docker 部署 RocketMQ 时的两个核心问题:

  1. 生产者超时:通过配置 brokerIP1 让 Broker 注册宿主机 IP
  2. Console 连不上:通过 --network host 让 Console 直接访问宿主机网络