RocketMq之NameSrv路由删除

155 阅读1分钟

Broker被删除的方式有两种,一种是自己下线发送unregisterBroker删除命令,一种是NameSrv定时任务10秒检查一次所有Broker的心跳时间是否超过120S,超过自动进行剔除操作。

无论哪种方式,其最终都是调用到RouteInfoManager的unRegisterBroker方法。

这里有个很厉害的思路,我们从RouteInfoManager定时扫描清除Broker的方法开始分析

public void scanNotActiveBroker() {
    try {
        log.info("start scanNotActiveBroker");
        for (Entry<BrokerAddrInfo, BrokerLiveInfo> next : this.brokerLiveTable.entrySet()) {
            //获取Broker最后的更新时间
            long last = next.getValue().getLastUpdateTimestamp();
            //120s
            long timeoutMillis = next.getValue().getHeartbeatTimeoutMillis();
            //是否超时未续约心跳
            if ((last + timeoutMillis) < System.currentTimeMillis()) {
                //关闭通道
                RemotingHelper.closeChannel(next.getValue().getChannel());
                log.warn("The broker channel expired, {} {}ms", next.getKey(), timeoutMillis);
                //删除注册的集群,主体,Broker信息
                this.onChannelDestroy(next.getKey());
            }
        }
    } catch (Exception e) {
        log.error("scanNotActiveBroker exception", e);
    }
}
public void onChannelDestroy(BrokerAddrInfo brokerAddrInfo) {
    UnRegisterBrokerRequestHeader unRegisterRequest = new UnRegisterBrokerRequestHeader();
    boolean needUnRegister = false;
    if (brokerAddrInfo != null) {
        try {
            try {
                //加写锁
                this.lock.readLock().lockInterruptibly();
                //获取需要删除的信息,这个方法只是将需要删除的信息
                //加入到unRegisterRequest,并无其他操作
                needUnRegister = setupUnRegisterRequest(unRegisterRequest, brokerAddrInfo);
            } finally {
                this.lock.readLock().unlock();
            }
        } catch (Exception e) {
            log.error("onChannelDestroy Exception", e);
        }
    }
    //有需要删除的信息
    if (needUnRegister) {
        //这里是重点,跟进去
        boolean result = this.submitUnRegisterBrokerRequest(unRegisterRequest);
        log.info("the broker's channel destroyed, submit the unregister request at once, " +
            "broker info: {}, submit result: {}", unRegisterRequest, result);
    }
}
public boolean submitUnRegisterBrokerRequest(UnRegisterBrokerRequestHeader unRegisterRequest) {
    //做法:将需要删除的信息放入一个阻塞队列就完事了,然后会有线程从这个队列
    //拉取数据进行真正的删除操作。在阿里系的源码中这个操作十分常见,比如nacos
    return this.unRegisterService.submit(unRegisterRequest);
}
public boolean submit(UnRegisterBrokerRequestHeader unRegisterRequest) {
    //放入阻塞队列
    return unregistrationQueue.offer(unRegisterRequest);
}

接下来让我们找一下消费的方法,点击unregistrationQueue就可以看到有一个take方法。

在BatchUnregistrationService类中的run方法中是消费的方法,这里还有一个很厉害的操作,仔细看,好好学。

public void run() {
    while (!this.isStopped()) {
        try {
            //首先take方法,这里是一个阻塞方法,也就是有数据才往下走,没有数据就阻塞。
            final UnRegisterBrokerRequestHeader request = unregistrationQueue.take();
            
            Set<UnRegisterBrokerRequestHeader> unregistrationRequests = new HashSet<>();
            //将queue中的所有数据转移到unregistrationRequests中来。
            //利用take+drainTo方法来完成取一次取所有的操作,很帅!
            unregistrationQueue.drainTo(unregistrationRequests);

            // Add polled request
            //将第一个请求放入进来
            unregistrationRequests.add(request);
            //做真正的删除操作,这里就没什么好看的了,一堆容器删除逻辑
            this.routeInfoManager.unRegisterBroker(unregistrationRequests);
        } catch (Throwable e) {
            log.error("Handle unregister broker request failed", e);
        }
    }
}