RocketMQ 发送全部失败:日志指向 10909,Broker 只监听了 10911
故障时间线
订单服务灰度上线。5 分钟后,用户反馈「下单后收不到物流通知」——不是延迟,是完全收不到。监控显示消息发送成功率 0%。
先查 Broker:CPU 35%、磁盘 util 12%、网络延迟 1ms,指标都在正常区间。但下游物流、积分、推送系统明确反馈:没有收到任何订单状态变更。支付链路本身正常,交易已落库,断开的是后续的业务闭环。
应用日志:connect refused,端口 10909
日志特征高度一致:
org.apache.rocketmq.remoting.exception.RemotingConnectException: connect to <192.168.1.100:10909> failed
MQClientException: Send [3] times, still failed, cost [9495]ms
端口是 10909,而不是 RocketMQ 默认的 10911。错误类型是 connect refused,意味着 TCP 连接被主动拒绝。
这和队列排队超时后被 BrokerFastFailure 清理的 SYSTEM_BUSY 不同:那种情况 Broker 还在处理,只是处理不过来;这次连接根本没有建立起来。
第一步:先确认 Broker 端
在 Broker 机器上查看监听端口:
netstat -tlnp | grep -E '1091[0-9]'
只看到 10911 在监听,没有 10909。日志里没有 SYSTEM_BUSY、没有 reject、没有 FullGC。磁盘:
iostat -x 1
util 12%,没有压力。
Broker 端没有问题,问题出在客户端连接的目标端口上。
telnet 验证:30 秒排除网络层
telnet 192.168.1.100 10911
telnet 192.168.1.100 10909
10911 通,10909 不通。结论:10909 不通不是防火墙拦截,也不是网络不可达——refused 说明目标主机收到了连接请求但主动拒绝,因为没有进程在该端口监听。
排查决策树
RocketMQ 发送失败
├── 看报错端口
│ ├── 10909(VIP 端口)
│ │ ├── telnet 不通 → 检查 vipChannelEnabled 配置
│ │ └── telnet 通 → 查 Broker 线程池/流控
│ └── 10911(普通端口)→ 常规排查
└── 确认网络层后再查中间件层
telnet 两个端口花了 30 秒。如果没看端口直接去翻 Broker 配置,定位时间会明显拉长。排查中间件问题,第一步不是查中间件配置,而是先看应用日志里报的是哪个端口。
端口从哪来:vipChannelEnabled 默认 true
按 Apache RocketMQ 官方客户端配置文档,ClientConfig 中 vipChannelEnabled 的含义是「是否启用 vip netty 通道以发送消息」,默认值取自 -D com.rocketmq.sendMessageWithVIPChannel 参数;未设置该参数时默认为 true。
VIP 通道使用的端口是 broker listenPort - 2。Broker 默认端口 10911,haListenPort 默认 10912,因此 VIP 端口默认是 10909。
也就是说,客户端默认会去连 10909。这次灰度版本的链路就是:
配置开关 → 地址选择变化 → 端口不可达 → 连接全部失败。
云厂商文档对此有明确说法。腾讯云 TDMQ 文档指出:4.x 客户端开启 VIPChannel 后,SDK 会与 Broker 的指定端口建立连接,该功能对云上集群不适用,开启后会出现连接失败,需要在初始化时调用 producer.setVipChannelEnabled(false) 与 consumer.setVipChannelEnabled(false)。apache/rocketmq issue #568 里也是类似结论:客户端 telnet 9876 通、Broker 端有 IP 记录,但发消息超时,社区建议关闭 VIP 通道或开放 10909。
两条修复路径
路径一:服务端开放 10909
在 Broker 侧放行 10909,并确认 10912 HA 端口的策略,让客户端默认的 VIP 通道能连上。
取舍:改动集中在服务端,客户端代码不动,一次配置对所有应用生效。代价是防火墙/安全组要额外开一个端口,且需要与云厂商的集群策略对齐;如果 Broker 是托管集群,未必允许你改监听和策略。
路径二:客户端关闭 VIP 通道
// Producer
producer.setVipChannelEnabled(false);
// Consumer
consumer.setVipChannelEnabled(false);
取舍:不用动服务端网络策略,改的是应用初始化代码。代价是每一处生产/消费客户端都要改,多团队多仓库时容易漏;这是代码改动,要走发布流程,跟客户端升级、回滚的兼容性也要一并考虑。如果用 4.x 客户端连云上集群,这条路径与云厂商的建议一致。
一个还没结论的点
5.x 客户端走 Proxy 之后,vipChannelEnabled 这个开关的行为和默认值与 4.x 不同,具体取决于客户端版本和接入方式。遇到同类报错时,需要按实际依赖的版本自行确认,不能直接套用上面的默认值结论。