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);
}
}
}