Nacos源码4:Nacos缓存service实例Client对象逻辑

152 阅读4分钟

首先,我们先统一各个角色的称呼:

  • 这里服务提供者实例,我们统一称为client端,每个实例的唯一标识我们称为clientId,即ip/port信息。
  • NacosServer端,我们统一称为server端

注册服务时,NacosServer会为每个注册服务的实例节点维护(缓存)一个Client对象,这个Client对象主要用于:

  • 封装(缓存)服务实例列表

  • 根据Client对象是否为null来判断client与NacosServer端的连接是否断开,如果连接断开则不能注册服务

在前面《Nacos服务发现基本原理》的文章中,介绍到获取service的实例的流程:

  • 首先获取注册service的各个实例节点ip/port集合。至于这个service->实例节点集合是何时怎样缓存的,前文《Nacos缓存注册service的实例节点集合》有说明。其实这一步相当于是根据service获取了实例的index索引信息,后面步骤就是根据这个实例的index索引信息,返回缓存的各个实例。
  • 然后根据service实例的clientId(ip/port信息)获取Client对象。
 // 根据service实例节点获取对应的Client对象
 Client client = clientManager.getClient(clientId);
  • 最后由于这个Client对象缓存了真正的实例,所以从Client对象返回缓存的service的实例

我们看到第2步,根据实例节点获取对应的Client对象。那么这个实例Client对象是何时缓存的??又是怎样缓存的???

我们猜想:这个实例Client对象应该是在service注册时,在NacosServer端缓存的。

所以,我们看看NacosServer处理注册的流程。

一、缓存服务实例Client对象的基本流程

1.1 判断是否存在client对象

 private void createIpPortClientIfAbsent(String clientId) {
        if (!clientManager.contains(clientId)) {
            ...
            clientManager.clientConnected(clientId, clientAttributes);
        }

如果不存在client对象,则创建client对象,并维护(缓存)client对象,其实也是维护着与client的连接。

1.2 封装为一个Client对象

基于注册service的实例节点的clientId(即ip/port信息)封装为一个Client对象。这个Client对象含有注册服务的这些实例的ip/port信息。

    @Override
    public IpPortBasedClient newClient(String clientId, ClientAttributes attributes) {
        long revision = attributes.getClientAttribute(REVISION, 0);
        // 基于注册service的实例节点的clientId(即ip/port信息)封装为一个Client对象
        IpPortBasedClient ipPortBasedClient = new IpPortBasedClient(clientId, true, revision);
        ipPortBasedClient.setAttributes(attributes);
        return ipPortBasedClient;
    }

注意:clientId指的时注册服务的实例节点的ip/port信息。

1.3 然后缓存clientId->Client对象

    @Override
    public boolean clientConnected(final Client client) {
        // 缓存clientId->Client对象
        clients.computeIfAbsent(client.getClientId(), s -> {
            Loggers.SRV_LOG.info("Client connection {} connect", client.getClientId());
            IpPortBasedClient ipPortBasedClient = (IpPortBasedClient) client;
            ipPortBasedClient.init();
            return ipPortBasedClient;
        });
        return true;
    }

1.4 获取Client对象

前文我们说过,当在服务消费端在发现获取服务的时候:NacosServer端会根据clientId获取Client对象

所以,就能从上面缓存的clients中获取Client对象

    @Override
    public Client getClient(String clientId) {
        return clients.get(clientId);
    }

之所以获取Client对象,是因为Client对象中封装了service的实例列表信息。所以最后发现获取服务实例的时候,从Client对象返回缓存的service的实例。

1.5 校验client连接是否断开

如果连接断开,是不能注册服务的。

private void checkClientIsLegal(Client client, String clientId) {
    if (client == null) {
        Loggers.SRV_LOG.warn("Client connection {} already disconnect", clientId);
        throw new NacosRuntimeException(NacosException.CLIENT_DISCONNECT,
                String.format("Client [%s] connection already disconnect, can't register ephemeral instance.",
                        clientId));
    }
    ...

二、最后总结

首先,我们先统一各个角色的称呼:

  • 这里服务提供者实例,我们统一称为client端,每个实例的唯一标识我们称为clientId,即ip/port信息。
  • NacosServer端,我们统一称为server端

当client发送服务注册请求,server收到请求之后:

  • 注册服务之前为每个client维护(缓存)一个Client对象,这个Client对象实际上就是注册service的各个实例节点对应的Client对象。缓存的实际上就是保存:clientId -> Client对象的映射关系缓存到Map中。这个clientId就是注册service的实例节点的唯一标识(ip/port信息)。同时这个Client对象在整个服务注册/发现的流程中还作为判断client连接是否断开的判断依据,可以说这个Client对象代表的就是client连接,维护着client的连接。
  • 判断client是否存在(即与client之间的连接是否存在)
  • 注册服务
  • 注册服务之后,异步发送服务注册事件:即维护(缓存)service->clientId映射关系

判断Client连接是否断开:

 private void checkClientIsLegal(Client client, String clientId) {
        // 其实就是判断获取到的client是否为null来判断client连接是否断开
        if (client == null) {
            Loggers.SRV_LOG.warn("Client connection {} already disconnect", clientId);
            throw new NacosRuntimeException(NacosException.CLIENT_DISCONNECT,
                    String.format("Client [%s] connection already disconnect, can't register ephemeral instance.",
                            clientId));
        }

每个注册服务的实例都有一个Client对象与之一 一对应。这个Client对象里面封装了具体实例信息,便于服务发现获取时,从此Client对象中获取实例列表。

所以server端维护Client对象的流程就是这样:

  1. 服务提供者发起注册请求
  2. NacosServer根据服务提供者实例的clientId(ip/port信息)生成Client对象
  3. 缓存clientId->Client对象的映射关系到Map中