shihaomq opened a new issue, #10812:
URL: https://github.com/apache/rocketmq/issues/10812

   ### Before Creating the Bug Report
   
   - [x] I found a bug, not just asking a question, which should be created in 
[GitHub Discussions](https://github.com/apache/rocketmq/discussions).
   
   - [x] I have searched the [GitHub 
Issues](https://github.com/apache/rocketmq/issues) and [GitHub 
Discussions](https://github.com/apache/rocketmq/discussions)  of this 
repository and believe that this is not a duplicate.
   
   - [x] I have confirmed that this bug belongs to the current repository, not 
other repositories of RocketMQ.
   
   
   ### Runtime platform environment
   
   4.19.91-007.ali4000.alios7.x86_64
   
   ### RocketMQ version
   
   rocketmq 5.2.0
   
   ### JDK Version
   
   java 11
   
   ### Describe the Bug
   
   根因
   
      ReplicasManager.sendHeartbeatToController() 只向 
availableControllerAddresses(由 scanAvailableControllerAddresses() 通过 TCP 
可达性检查填充)发送心跳,不向 controllerLeaderAddress
      
发送心跳,即使该地址已被发现并被其他方法(getReplicaInfo、registerBrokerToController、brokerElect、alterSyncStateSet)成功使用。
   
      当 DR 容灾切换导致短暂网络隔离时,Leader 的 IP 从 availableControllerAddresses 中被移除,且可能因 
Netty Channel 生命周期问题无法重新加入。此后 Broker 只向 LEARNER/Follower Controller
      发送心跳,而这些节点无法处理 Raft 事件(onBrokerHeartbeat 返回 
CONTROLLER_NOT_LEADER,心跳被静默丢弃)。
   
      
   <img width="2996" height="1192" alt="Image" 
src="https://github.com/user-attachments/assets/1da4c322-5c5a-49e0-8cb5-a5476f999eca";
 />
   
   ### Steps to Reproduce
   
   复现步骤
   
      1. 部署跨机房 RocketMQ 集群,启用 Controller 模式(Zone A = PRIMARY,Zone B = SECONDARY)
      2. 隔离 Zone A 网络(模拟主机房孤岛)→ Controller Leader 切换到 Zone B
      3. 恢复 Zone A 网络
      4. 观察 Zone A 的 SLAVE Broker 无法进入 SyncStateSet,拓扑查询接口返回 healthy=false
   
      关键日志证据
   
      1. Broker 端:Zone B Controller 地址在隔离期间被移除,之后未恢复
       2026-07-28 17:21:45 WARN ReplicasManager_scan_thread_1 - 
scanAvailableControllerAddresses remove unconnected address 10.83.32.236:9878
       2026-07-28 17:21:45 WARN ReplicasManager_scan_thread_2 - 
scanAvailableControllerAddresses remove unconnected address 10.83.31.207:9878
       2026-07-28 17:22:25 WARN ReplicasManager_scan_thread_6 - 
scanAvailableControllerAddresses remove unconnected address 10.83.32.235:9878
      总共只有 3 条移除记录(全部在启动当天)。之后未重新加入。
   
      2. Broker 端:Controller Leader 地址能正确发现(通过 LEARNER 查询)
       2026-08-04 09:10:56 INFO ReplicasManager_ScheduledService_2 - Update 
controller leader address to 
mq-mryarmrajcf-controller-1-2.mq-mryarmrajcf-controller-svc-headless.rocketmq:9878
      每 ~10 秒更新一次,始终指向 Zone B 的 
LEADER。getReplicaInfo、registerBrokerToController 等方法使用此地址均正常工作。
   
      3. Zone A LEARNER Controller:收到了 SLAVE 心跳(但无法处理)
       2026-08-04 09:10:47 INFO ControllerRequestExecutorThread_14 - broker 
BrokerIdentityInfo{clusterName='cluster-mryarmrajcf', 
brokerName='cluster-mryarmrajcf_broker-0', brokerId=2} heart beat
       2026-08-04 09:10:48 INFO ControllerRequestExecutorThread_6 - broker 
BrokerIdentityInfo{clusterName='cluster-mryarmrajcf', 
brokerName='cluster-mryarmrajcf_broker-1', brokerId=1} heart beat
      SLAVE 心跳(broker-0/brokerId=2、broker-1/brokerId=1)到达了 LEARNER,但 LEARNER 返回 
CONTROLLER_NOT_LEADER → 心跳被静默丢弃,未存入 brokerLiveTable。
   
      4. Zone B LEADER Controller:只收到 MASTER 心跳,没有 SLAVE 心跳
       2026-08-04 09:10:43 INFO ControllerRequestExecutorThread_1 - broker 
BrokerIdentityInfo{clusterName='cluster-mryarmrajcf', 
brokerName='cluster-mryarmrajcf_broker-1', brokerId=2} heart beat
       2026-08-04 09:10:43 INFO ControllerRequestExecutorThread_10 - broker 
BrokerIdentityInfo{clusterName='cluster-mryarmrajcf', 
brokerName='cluster-mryarmrajcf_broker-0', brokerId=1} heart beat
      只有 MASTER 心跳(broker-0/brokerId=1、broker-1/brokerId=2)。没有 SLAVE 
心跳(broker-0/brokerId=2、broker-1/brokerId=1)。
   
      5. LEADER:因 SLAVE "don't alive" 拒绝 SyncStateSet 变更
       2026-08-04 09:10:38 ERROR JRaft-FSMCaller-Disruptor-0 - Rejecting alter 
syncStateSet request because the replicas {2} don't alive
       2026-08-04 09:10:38 ERROR JRaft-FSMCaller-Disruptor-0 - Rejecting alter 
syncStateSet request because the replicas {1} don't alive
      6. LEADER:null channel 过期(由 scanNeedReelectBrokerSets 创建 null 
BrokerIdentityInfo 导致)
       2026-08-04 09:10:55 WARN RaftBrokerHeartbeatManager_scheduledService_1 - 
The broker channel null expired, brokerInfo 
BrokerIdentityInfo{clusterName='null', 
brokerName='cluster-mryarmrajcf_broker-1',
        brokerId=null}
       2026-08-04 09:11:00 WARN RaftBrokerHeartbeatManager_scheduledService_1 - 
The broker channel null expired, brokerInfo 
BrokerIdentityInfo{clusterName='null', 
brokerName='cluster-mryarmrajcf_broker-0',
        brokerId=null}
   
   ### What Did You Expect to See?
   
   111
   
   ### What Did You See Instead?
   
    问题所在 —— sendHeartbeatToController() 只使用 availableControllerAddresses:
       // ReplicasManager.java line 401-420(5.2.0 )
       public void sendHeartbeatToController() {
           final List<String> controllerAddresses = 
this.getAvailableControllerAddresses();
           for (String controllerAddress : controllerAddresses) {
               if (StringUtils.isNotEmpty(controllerAddress)) {
                   this.brokerOuterAPI.sendHeartbeatToController(
                       controllerAddress,  // ← 只从 availableControllerAddresses 
取
                       this.brokerConfig.getBrokerClusterName(),
                       ...
                       this.brokerControllerId,
                       ...
                   );
               }
           }
       }
      作为对比,controllerLeaderAddress 被其他所有 Controller RPC 方法使用:
       this.brokerOuterAPI.getReplicaInfo(this.controllerLeaderAddress, ...);   
        // line 644
       this.brokerOuterAPI.registerBrokerToController(..., 
controllerLeaderAddress);     // line 561
       this.brokerOuterAPI.brokerElect(this.controllerLeaderAddress, ...);      
        // line 378
       this.brokerOuterAPI.alterSyncStateSet(this.controllerLeaderAddress, 
...);        // line 761
      controllerLeaderAddress 通过 updateControllerMetadata() 发现——该方法向 
LEARNER/Follower Controller 发送查询(这些节点可以回答元数据查询,无需走 Raft)。但心跳处理需要 Raft 
Leader——只有 LEADER 能将
      onBrokerHeartbeat 事件提交到 brokerLiveTable。
   
      核心矛盾:LEARNER 能回答"谁是 Leader"(元数据查询,本地读),但不能处理心跳(需要 Raft 日志提交,只有 Leader 
能做)。availableControllerAddresses 中的 LEARNER 地址可以用来发现
      Leader,但不能用来处理心跳。而 Leader 的地址恰好不在 availableControllerAddresses 中。
   
   ### Additional Context
   
   _No response_


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to