文章目录
- 一、 为什么会出现“双网卡问题”?
- 关键冲突点:Broker 汇报了错误的“身份证”
- 二、 核心症状(怎么判断自己踩坑了?)
- 三、 完美解决方案
- 方案 A:如果是原生安装(修改 `broker.conf`)
- 方案 B:如果是 Docker / Docker Compose 安装(最推荐)
- 四、 必须要放行的“云服务器安全组”端口
- 总结口诀
在分布式中间件(如 RocketMQ、Kafka 等)的部署中,“双网卡问题”(或者叫内外网隔离、多网络接口问题)是把中间件部署在远端云服务器,而本地 Spring Boot 去连接时最经典、最容易让人抓狂的坑。
它的核心表现通常是:明明 Nacos 能连上,RocketMQ 的 Namesrv 也能连上,但一发送消息或者消费消息就直接报超时(Timeout)或连接拒绝(Connection Refused)。
下面我们用最通俗的语言,彻底拆解这个问题的底层原理和解决方案。
一、 为什么会出现“双网卡问题”?
在阿里云等云服务器(ECS)上,通常存在两块“虚拟网卡”:
- 内网网卡(Private IP):比如
172.19.xx.xx。这是云厂商机房内部通信用的,速度极快(内网千兆/万兆),且不花流量费。 - 公网网卡(Public IP):比如
47.98.xx.xx。这是暴露给互联网的,你本地电脑连接服务器、以及用户访问网站都走这个 IP。
关键冲突点:Broker 汇报了错误的“身份证”
RocketMQ 的架构是:NameServer(注册中心)负责管理路由,Broker(数据节点)负责真正存取消息。
- 默认情况:当你在云服务器上启动 RocketMQ Broker 时,它默认会去抓取服务器的内网 IP(因为在服务器看来,内网网卡才是它的主网卡)。
- 注册阶段:Broker 向 NameServer 报到,说:“我叫 Broker-A,我的地址是
172.19.xx.xx:10911。” - 本地连接:你本地的 Spring Boot 启动了,配置了 NameServer 的公网 IP(
47.98.xx.xx)。连接成功! - 死锁发生:Spring Boot 问 NameServer:“我要发消息,请问 Broker 在哪?” NameServer 极为诚实地把刚才 Broker 注册的地址丢给本地:“Broker 在
172.19.xx.xx:10911,你去吧。” - 崩溃:你的本地电脑在互联网上,怎么可能直接访问到阿里云机房内部的
172.19.xx.xx呢?于是,本地 Spring Boot 开始疯狂尝试连接这个内网 IP,直到连接超时报错。
二、 核心症状(怎么判断自己踩坑了?)
如果你遇到以下现象,99% 就是双网卡/内外网路由问题:
- 测试连接正常:用
telnet 47.98.xx.xx 9876(NameServer 端口)是通的。 - 控制台正常:部署在同台服务器上的 RocketMQ Dashboard(控制台)能正常看到集群和 Topic。
- 唯独本地代码报错:本地 Spring Boot 生产者发送消息时,日志卡住,几秒后抛出
RemotingConnectException: connect to <172.19.xx.xx:10911> failed。注意看报错信息里的 IP,如果变成了服务器的内网 IP,就是此问题。
三、 完美解决方案
解决这个问题的核心逻辑非常简单:强行指定 Broker 对外宣布的“身份证”为公网 IP。
针对你使用Docker或原生安装,修改方式略有不同:
方案 A:如果是原生安装(修改broker.conf)
找到你 RocketMQ 的配置文件(通常在conf/2m-2s-async/或自定义路径下的broker.conf),在文件末尾显式添加一行配置:
# 关键配置:强行指定 Broker 对外暴露的 IP 为你的云服务器公网 IP brokerIP1 = 47.98.xx.xx # 如果你要做主从,或者有特殊监听需求,可以把 brokerIP2 也配上,一般配 brokerIP1 即可 # brokerIP2 = 47.98.xx.xx注意:启动 Broker 的时候,必须带上-c参数指定这个配置文件,否则配置不生效:
nohupshmqbroker-nlocalhost:9876-c../conf/broker.conf&方案 B:如果是 Docker / Docker Compose 安装(最推荐)
如果你是用 Docker 部署的,由于 Docker 容器内部还有一套虚拟网络,更容易触发这个问题(Broker 会把 Docker 的容器内网 IP 如172.17.0.x注册过去)。
在 Docker 启动命令中,通过-c或者环境变量将brokerIP1传进去。
Docker Run 示例:
dockerrun-d\--namermqbroker\-p10911:10911-p10909:10909\vincenzopalazzo/rocketmq:v5.1.0\shmqbroker-n47.98.xx.xx:9876-c/opt/rocketmq/conf/broker.conf--param"brokerIP1=47.98.xx.xx"Docker Compose 示例(最直观):
在映射的broker.conf挂载文件中写死brokerIP1=你的公网IP,或者在command启动命令中注入:
version:'3.5'services:rmqnamesrv:image:apache/rocketmq:5.1.0container_name:rmqnamesrvports:-9876:9876command:sh mqnamesrvrmqbroker:image:apache/rocketmq:5.1.0container_name:rmqbrokerports:-10909:10909-10911:10911environment:-NAMESRV_ADDR=rmqnamesrv:9876# 核心就在最后这一句,强行指定当前 Broker 注册上去的 IP 是公网公网 IPcommand:sh mqbroker-c /home/rocketmq/rocketmq-5.1.0/conf/broker.conf--param "brokerIP1=47.98.xx.xx"depends_on:-rmqnamesrv四、 必须要放行的“云服务器安全组”端口
改完配置后,本地要成功通信,还需要在阿里云/腾讯云后台放行以下几个端口。RocketMQ 用的端口比普通中间件多,少放一个都会失败:
9876:NameServer 的端口(本地 Spring Boot 建立初步连接、拉取路由表用)。10911:Broker 的默认监听端口(本地非 VIP 通道发送/消费消息的核心通道)。10909:Broker 的VIP 通道端口(RocketMQ 默认开启 VIP 通道,如果你的客户端没显式关闭 VIP 通道,消息会往这个端口发。很多人只放行了 10911 没放行 10909,结果依然超时)。8080/8081(针对 RocketMQ 5.x 纯 gRPC 模式):如果你使用的是最新的 RocketMQ 5.x 并且使用了新的 Proxy 模式,走的是 gRPC 协议,需要开放对应的 Proxy 端口(默认 8081)。
总结口诀
本地连远端,Broker 别偷懒;
莫报内网号,公网 IP 填上面(brokerIP1);
九八七六连名字,一零九一送消息;
还有一零九零九,安全组里别漏失!