«

Nacos源码学习计划-Day24-Nacos2.x-服务端心跳健康实例检测

ZealSinger 发布于 阅读:46 技术文档


今天我们的主要内容就是Nacos2.x版本下,服务端的健康检测逻辑。因为Nacos升级后使用了gRPC,导致这个部分逻辑肯定时发生了较大的变化。

注意,这里只是说服务端,即服务都安如何去检测客户端的健康状态,而不是客户端的心跳以及也不是集群中对于每个节点的健康检测。

我们可以来看到ConnectionManager,在这个类的内部有一个Map保存了所有的了所有的Connection连接对象,连接对象有个元数据的成员对象ConnectionMeta,他有一个成员属性为lastActiveTime(最后存活时间)

image-20260120211513211

ConnectionManager对象中,有个@PostConstruct注解标记的start方法,该方法会开启一个run任务,在该方法中,遍历上述的map并且用now和元数据中的lastActiveTime进行比较判断是否存活/过时,如果发现过时了,就会放入到outDatedConnections这个Set集合中

image-20260120212234516

outDatedConnections在没有超出上限的时候,不会立马被清除,而是会统一在最后进行二次判断是否真正下线,如果没有下线,则会刷新activeTime并且加入到存活对象集合中,这个过程为了保证并发安全还是用了countDownLatch,这个被称之为探活机制

image-20260120212652183

如果上述二次请求遍历完毕后依旧集合中依旧还有数据,则调用unregister()取消注册进行删除

image-20260120212840093

unregister源码如下,首先会从connections这个管理了所有的连接的Map中remove,remove的时候会返回对应的Connection连接实例信息,根据实例信息中的Ip信息操作连接数,最后通知客户端注销连接

public synchronized void unregister(String connectionId) {
   // 移除客户端信息
   Connection remove = this.connections.remove(connectionId);
   if (remove != null) {
       String clientIp = remove.getMetaInfo().clientIp;
       AtomicInteger atomicInteger = connectionForClientIp.get(clientIp);
       if (atomicInteger != null) {
           int count = atomicInteger.decrementAndGet();
           if (count <= 0) {
               connectionForClientIp.remove(clientIp);
          }
      }
       remove.close();
       Loggers.REMOTE_DIGEST.info("[{}]Connection unregistered successfully. ", connectionId);
       // 通知客户端注销连接
       clientConnectionEventListenerRegistry.notifyClientDisConnected(remove);
  }
}

来看一下通知客户端注销连接的逻辑,可以看到最后是发布了一个ClientDisconnectEvent的事件,这个事件的处理我们在上一节的内容中分析过了,客户端会注销,同步到集群,当前节点关于该链接的数据,订阅者列表,注册表信息等等也都会被删除

// 1
clientConnectionEventListenerRegistry.notifyClientDisConnected(remove);

// 2
public void notifyClientDisConnected(final Connection connection) {
   
   for (ClientConnectionEventListener clientConnectionEventListener : clientConnectionEventListeners) {
       try {
       
           // 客户端注销,我们看这个实现类,ConnectionBasedClientManager
           clientConnectionEventListener.clientDisConnected(connection);
      } catch (Throwable throwable) {
           Loggers.REMOTE.info("[NotifyClientDisConnected] failed for listener {}",
                   clientConnectionEventListener.getName(), throwable);
      }
  }
}

// 3 ConnectionBasedClientManager 实现类
@Override
public void clientDisConnected(Connection connect) {
   clientDisconnected(connect.getMetaInfo().getConnectionId());
}

// 4
@Override
public boolean clientDisconnected(String clientId) {
   Loggers.SRV_LOG.info("Client connection {} disconnect, remove instances and subscribers", clientId);
   ConnectionBasedClient client = clients.remove(clientId);
   if (null == client) {
       return true;
  }
   client.release();
   // 最后发布客户端注销事件
   NotifyCenter.publishEvent(new ClientEvent.ClientDisconnectEvent(client));
   return true;
}

编程 Java 项目