nacos总结

本文介绍了Nacos 2.0的客户端模型,包括Client、健康检查机制、服务注册和发现的过程。客户端通过长连接进行服务实例注册与订阅,健康检查依赖于Grpc协议的长连接。集群间数据同步通过定时任务和Client全量数据交换实现,以保持一致性。此外,还详细阐述了服务注册中心的模型和索引管理。
摘要由CSDN通过智能技术生成

Client

从Nacos2.0以后,新增了Client模型,管理与该客户端有关的数据内容,如果一个客户端发布了一个服务,那么这个客户端发布的所有服务和订阅者信息都会被更新到一个Client对象中,这个Client对象对应于这个客户端的链接,然后通过事件机制触发索引信息的更新。Client负责管理一个客户端的服务实例注册Publish和服务订阅Subscribe,可以方便地对需要推送的服务范围进行快速聚合,同时一个客户端gRPC长连接对应一个Client,每个Client有自己唯一的 clientId

ConnectionBasedClientManager负责管理长连接clientId与Client模型的映射关系

客户端重试机制

由于网络的不稳定,RPC 请求可能失败,那么失败了就得有保障措施,比如说请求重试。

Nacos 中的服务注册的请求重试就是通过 RedoService 实现的。

其原理为:当注册服务等操作时,先将请求缓存到 map 中,然后定时任务每隔3秒检测一次,将需要重试的任务重新发起请求

健康检查机制

  • 在2.0版本以后,持久实例不变,临时实例而是通过长连接来判断实例是否健康。

  • 长连接: 一个连接上可以连续发送多数据包,在连接保持期间,如果没有数据包发送,需要双方发链路检测包,在Nacos2.0之后,使用Grpc协议代替了http协议。长连接会保持客户端和服务端发送的状态,在源码中ConnectionManager 管理所有客户端的长连接

    • ConnectionManager: 每3秒检测所有超过20S内没有发生过通讯的客户端,向客户端发起ClientDetectionRequest探测请求,如果客户端在1s内成功响应,则检测通过,否则执行unregister方法移除Connection
  • 如果客户端持续和服务端进行通讯,服务端是不需要主动下探的,只有当客户端没有一直和服务端通信的时候,服务端才会主动下探操作

@Service
public class ConnectionManager extends Subscriber<ConnectionLimitRuleChangeEvent> {

Map<String, Connection> connections = new ConcurrentHashMap<String, Connection>();

   //只要spring容器启动,会触发这个方法
    @PostConstruct
    public void start() {
    // 启动不健康连接排除功能.
    RpcScheduledExecutor.COMMON_SERVER_EXECUTOR.scheduleWithFixedDelay(new Runnable() {
      @Override
      public void run() {
        // 1. 统计过时(20s)连接
         Set<Map.Entry<String, Connection>> entries = connections.entrySet();
        //2.获得需要剔除的IP和端口
        //3.根据限制获取剔除的IP和端口
        //4.如果还是有需要剔除的客户端,则继续执行
        //5.没有活动的客户端执行探测            
        //6.如果没有马上响应,则马上剔除
        //7.剔除后发布ClientDisconnectEvent事件
      }
    });

    }
}

//注销(移出)连接方法
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);
}
当服务端操作移除事件以后,会操作notifyClientDisConnected()方法,主要调用的是 clientConnectionEventListener.clientDisConnected(connection)方法,将连接信息传入进去

public void notifyClientDisConnected(final Connection connection) {

for (ClientConnectionEventListener clientConnectionEventListener : clientConnectionEventListeners) {
    try {
        clientConnectionEventListener.clientDisConnected(connection);
    } catch (Throwable throwable) {
        Loggers.REMOTE.info("[NotifyClientDisConnected] failed for listener {}",
                clientConnectionEventListener.getName(), throwable);
    }
}
        
clientConnectionEventListenerd的实现类是ConnectionBasedClientManager,在这里面会出发清除索引缓存等操作

@Component("connectionBasedClientManager")
public class ConnectionBasedClientManager extends ClientConnectionEventListener implements ClientManager {
    @Override
    public boolean clientDisconnected(String clientId) {
        Loggers.SRV_LOG.info("Client connection {} disconnect, remove instances and subscribers", clientId);
        //同步移除client数据
        ConnectionBasedClient client = clients.remove(clientId);
        if (null == client) {
            return true;
        }
        client.release();
        //服务订阅,将变更通知到客户端
        NotifyCenter.publishEvent(new ClientEvent.ClientDisconnectEvent(client));
        return true;
    }
}

ServiceManager

ServiceManagerNacos的服务管理器,内部维护了两个 ConcurrentHashMap 类型的成员变量,singletonRepository 用来保证Service的单例;
namespaceSingletonMaps 用来存储namespace下的所有 Service//保证单例Service
private final ConcurrentHashMap<Service, Service> singletonRepository;
//namespace下的所有service,存储Service的容器
private final ConcurrentHashMap<String, Set<Service>> namespaceSingletonMaps;

/**
 * 获取单例 Service
 */
public Service getSingleton(Service service) {
    // Service在singletonRepository中不存在,发布ServiceMetadataEvent事件,将Service设置到singletonRepository中
    singletonRepository.computeIfAbsent(service, key -> {
        NotifyCenter.publishEvent(new MetadataEvent.ServiceMetadataEvent(service, false));
        return service;
    });
    // 获取单例 Service
    Service result = singletonRepository.get(service);
    // 容器,存储 namespace 与 Service集 的映射关系
    namespaceSingletonMaps.computeIfAbsent(result.getNamespace(), namespace -> new ConcurrentHashSet<>());
    namespaceSingletonMaps.get(result.getNamespace()).add(result);
    return result;
}

集群间的数据同步

为了确保集群间数据一致,不仅仅依赖于数据发生改变时的实时同步,后台有定时任务做数据同步。

  • 在1.x版本中,责任节点每5s同步所有Service的Instance列表的摘要(md5)给非责任节点。非责任节点用对端传来的服务md5比对本地服务的md5,如果发生改变,需要反查责任节点。
  • 在2.x版本中,对这个流程做了改造,当责任节点的Client数据发生变更后,会同步这个Client的全量数据给其他非责任节点。非责任节点会更新Client信息。为了避免非责任节点上非直连的Client数据不一致,责任节点每5s向非责任节点发送核实数据,续租这些Client,来维持非责任节点的Client数据不过期。包含了Client全量数据;非责任节点定时扫描非直连的Client数据,如果超过30s没有续租,移除这些非直连的client。这样可以减少1.x版本中的反查。

集群接受客户端的注册或注销

  • 1.x中,所有客户端请求会经过DistroFilter,它会判断当前节点是否为责任节点,判断的方法:如果hash服务名然后和nacos节点数量取模,得到的值就是责任节点的下标。如果当前节点不是责任节点,则转发给责任节点处理,责任节点处理后,由当前节点返回客户端。
  • 在2.x中,DistroFilter对于客户端就没用了,因为客户端与服务端会建立长连接,当前nacos节点是否是责任节点,取决于Client身上的isNative属性。如果是客户端直接注册在这个nacos节点上的,它的isNative属性为true,如果是由Distro协议,由集群中其他节点同步过来的,那么它的isNative属性为false。

注册中心模型

  • Service:服务,namespace+group+name=单例Service。Service与Instance不会直接发生关系,由ServiceManager管理

  • Instance:实例,InstancePublishInfo,由Client管理。

  • Client:一个客户端长连接对应一个Client,一个Client持有对应客户端注册和监听的的Service&Instance。Client使Service和Instance发生关联,由ClientManager管理。

  • Connection:连接(长连接),一个Connection对应一个Client,由ConnectionManager管理。

模型索引:Service与Instance没有直接关系,需要通过遍历所有Client注册的服务和实例,得到Service下所有Instance。为了加速查询,提供了两个索引服务

  • ClientServiceIndexesManager:Service->Client,服务与发布这个服务&服务与监听这个服务的客户端的关联关系。
  • ServiceStorage:Service->Instance,服务与服务下实例的关联关系。

服务注册

对于客户端来说,临时实例注册,走gRPC;持久实例注册走http。

对于服务端来说,无论是gRPC还是http,底层流程都是一样的:

  1. 建立Connection->Client->Service->Instance的关系
  2. 构建索引,用于辅助查询
  3. 通知订阅客户端
  4. 集群数据同步

服务发现

服务查询,走ServiceStorage索引服务。如果ServiceStorage查询不到数据,走复杂查询逻辑,然后再放入ServiceStorage缓存。当服务发生变更时,ServiceStorage缓存数据会更新。

服务订阅,服务端会把Client订阅的服务,注册到Client里管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值