Nacos源码学习系列服务端第2篇服务注册之客户端管理

目录

Client 模块的整体UML图

客户端管理器接口(ClientManager)

客户端管理器代理(ClientManagerDelegate)

 管理Ephemeral类型节点的客户端管理器(EphemeralIpPortClientManager )

客户端对象(IpPortBasedClient)

总结


官方对Client说明:
The abstract concept of the client stored by on the server of Nacos naming module. It is used to store which
services the client has published and subscribed
The client is bind to the ip and port users registered. It's a abstract content to simulate the tcp session client.

简单来说 服务提供者和服务消费者以及其他的集群节点都属于Client

服务提供者和服务消费者属于临时节点 集群节点 属于非临时节点

Client 模块的整体UML图

Client 相关内容主要包括几大核心类:

ClientManager 存储所有的客户端信息 以及客户端的crud功能 以及提供定时任务清理过期的客户端 

ClientManagerDelegate:统一客户端管理器的代理类

EphemeralIpPortClientManager:管理临时节点的客户端管理器

IpPortBasedClient:基于ip和端口的客户端

客户端管理器接口(ClientManager)

public interface ClientManager {
    
    /**
     * New client connected.
     *
     * @param clientId new client id
     * @param attributes client attributes, which can help create client
     * @return true if add successfully, otherwise false
     */
    boolean clientConnected(String clientId, ClientAttributes attributes);
    
    /**
     * New client connected.
     *
     * @param client new client
     * @return true if add successfully, otherwise false
     */
    boolean clientConnected(Client client);
    
    /**
     * Client disconnected.
     *
     * @param clientId client id
     * @return true if remove successfully, otherwise false
     */
    boolean clientDisconnected(String clientId);
    
    /**
     * Get client by id.
     *
     * @param clientId client id
     * @return client
     */
    Client getClient(String clientId);
    
    /**
     * Whether the client id exists.
     *
     * @param clientId client id
     * @return client
     */
    boolean contains(final String clientId);
    
    /**
     * All client id.
     *
     * @return collection of client id
     */
    Collection<String> allClientId();
    
    /**
     * Whether the client is responsible by current server.
     *
     * @param client client
     * @return true if responsible, otherwise false
     */
    boolean isResponsibleClient(Client client);
    
    /**
     * verify client.
     *
     * @param clientId client id
     * @return true if client is valid, otherwise is false.
     */
    boolean verifyClient(String clientId);
}

clientConnected:其实就是创建一个新的客户端并创建心跳检查任务

isResponsibleClient 这个方法含义:在一个集群里面判断该客户端健康检查是不是由当前的服务器负责 

verifyClient 这个方法目前么有看到有地方调用 我们暂且略过

ClientManager 的实现类由4个其中 

ClientManagerDelegate 统一的管理器代理类

客户端管理器代理(ClientManagerDelegate)

public class ClientManagerDelegate implements ClientManager {
    
    //
    private final ConnectionBasedClientManager connectionBasedClientManager;
    
    //临时节点的客户端管理器 
    private final EphemeralIpPortClientManager ephemeralIpPortClientManager;
    
    //持久化节点的客户端管理器
    private final PersistentIpPortClientManager persistentIpPortClientManager;
    
    //构造3个对象
    public ClientManagerDelegate(ConnectionBasedClientManager connectionBasedClientManager,
            EphemeralIpPortClientManager ephemeralIpPortClientManager,
            PersistentIpPortClientManager persistentIpPortClientManager) {
        this.connectionBasedClientManager = connectionBasedClientManager;
        this.ephemeralIpPortClientManager = ephemeralIpPortClientManager;
        this.persistentIpPortClientManager = persistentIpPortClientManager;
    }
    
    //新客户端加入
    @Override
    public boolean clientConnected(String clientId, ClientAttributes attributes) {
        return getClientManagerById(clientId).clientConnected(clientId, attributes);
    }
    
    //新客户端加入
    @Override
    public boolean clientConnected(Client client) {
        return getClientManagerById(client.getClientId()).clientConnected(client);
    }
    
    ...
    
    //客户端下线
    @Override
    public boolean clientDisconnected(String clientId) {
        return getClientManagerById(clientId).clientDisconnected(clientId);
    }
    
    //查找客户端
    @Override
    public Client getClient(String clientId) {
        return getClientManagerById(clientId).getClient(clientId);
    }
    
    //是否存在改客户端
    @Override
    public boolean contains(String clientId) {
        return connectionBasedClientManager.contains(clientId) || ephemeralIpPortClientManager.contains(clientId)
                || persistentIpPortClientManager.contains(clientId);
    }
    
    //客户端列表
    @Override
    public Collection<String> allClientId() {
        Collection<String> result = new HashSet<>();
        result.addAll(connectionBasedClientManager.allClientId());
        result.addAll(ephemeralIpPortClientManager.allClientId());
        result.addAll(persistentIpPortClientManager.allClientId());
        return result;
    }
    
    //该客户端的健康检查等是否有当前服务器负责
    @Override
    public boolean isResponsibleClient(Client client) {
        return getClientManagerById(client.getClientId()).isResponsibleClient(client);
    }
    
    //客户端校验
    @Override
    public boolean verifyClient(String clientId) {
        return getClientManagerById(clientId).verifyClient(clientId);
    }
    
    //根据clientId的格式选择不通的ClientManager
    private ClientManager getClientManagerById(String clientId) {
        if (isConnectionBasedClient(clientId)) {
            return connectionBasedClientManager;
        }
        //clientId以false结尾的是ephemeralIpPortClientManager
        //否则是 persistentIpPortClientManager 
        return clientId.endsWith(ClientConstants.PERSISTENT_SUFFIX) ? 
            persistentIpPortClientManager : ephemeralIpPortClientManager;
    }
    
    // clientId不包含#服务的客户端
    private boolean isConnectionBasedClient(String clientId) {
        return !clientId.contains(IpPortBasedClient.ID_DELIMITER);
    }
}

 管理Ephemeral类型节点的客户端管理器(EphemeralIpPortClientManager )

@Component("ephemeralIpPortClientManager")
public class EphemeralIpPortClientManager implements ClientManager {
    
    //clientId 为key Client 缓存
    private final ConcurrentMap<String, IpPortBasedClient> clients = new ConcurrentHashMap<>();
    
    //提供管理Nacos Server服务器集群功能 后面讲解到集群的时候详细介绍
    private final DistroMapper distroMapper;
    
    // IpPortBasedClient 的工厂类
    private final ClientFactory<IpPortBasedClient> clientFactory;
    
    public EphemeralIpPortClientManager(DistroMapper distroMapper, SwitchDomain 
        switchDomain) {
        this.distroMapper = distroMapper;
        //过期客户端清理任务 5s 执行一次 ExpiredClientCleaner
        GlobalExecutor.scheduleExpiredClientCleaner(new ExpiredClientCleaner(this, 
                switchDomain), 0,
                Constants.DEFAULT_HEART_BEAT_INTERVAL, TimeUnit.MILLISECONDS);
        //根据[ClientConstants.EPHEMERAL_IP_PORT]类型去工厂类获取相应的clientFactory
        clientFactory = ClientFactoryHolder.getInstance().
                        findClientFactory(ClientConstants.EPHEMERAL_IP_PORT);
    }
    
    //通过工厂类创建新的客户端
    @Override
    public boolean clientConnected(String clientId, ClientAttributes attributes) {
        return clientConnected(clientFactory.newClient(clientId, attributes));
    }
    
    //初始化客户端 并缓存到map中
    @Override
    public boolean clientConnected(final Client 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;
    }
    
    ....
    
    //从缓存中移除client 并发布 ClientEvent.ClientDisconnectEvent 事件到统一事件中心
    @Override
    public boolean clientDisconnected(String clientId) {
        Loggers.SRV_LOG.info("Client connection {} disconnect, remove instances and subscribers", clientId);
        IpPortBasedClient client = clients.remove(clientId);
        if (null == client) {
            return true;
        }
        NotifyCenter.publishEvent(new ClientEvent.ClientDisconnectEvent(client));
        client.release();
        return true;
    }
    
    @Override
    public Client getClient(String clientId) {
        return clients.get(clientId);
    }
    
    @Override
    public boolean contains(String clientId) {
        return clients.containsKey(clientId);
    }
    
    @Override
    public Collection<String> allClientId() {
        return clients.keySet();
    }
    
    //等后面降到集群的时候在说明distroMapper.responsible方法
    @Override
    public boolean isResponsibleClient(Client client) {
        if (client instanceof IpPortBasedClient) {
            return distroMapper.responsible(((IpPortBasedClient) client).getResponsibleId());
        }
        return false;
    }
    
    //刚方法在服务器集群间交互的时候用到 服务注册取消注册用不到目前
    @Override
    public boolean verifyClient(String clientId) {
        IpPortBasedClient client = clients.get(clientId);
        if (null != client) {
            NamingExecuteTaskDispatcher.getInstance()
                    .dispatchAndExecuteTask(clientId, new ClientBeatUpdateTask(client));
            return true;
        }
        return false;
    }
    
    /**
     * 该类的逻辑定期检查是否是一个过期的客户端 过期的含义满足下面任一个
     * 1、超时还没有发布服务 且 超时还没有订阅服务 
     * 2、超过了客户端的过期事件
     *  
     */
    private static class ExpiredClientCleaner implements Runnable {
        
        private final EphemeralIpPortClientManager clientManager;
        
        private final SwitchDomain switchDomain;
        
        public ExpiredClientCleaner(EphemeralIpPortClientManager clientManager, SwitchDomain switchDomain) {
            this.clientManager = clientManager;
            this.switchDomain = switchDomain;
        }
        
        @Override
        public void run() {
            long currentTime = System.currentTimeMillis();
            for (String each : clientManager.allClientId()) {
                IpPortBasedClient client = (IpPortBasedClient) clientManager.getClient(each);
                if (null != client && isExpireClient(currentTime, client)) {
                    clientManager.clientDisconnected(each);
                }
            }
        }
        
        private boolean isExpireClient(long currentTime, IpPortBasedClient client) {
            long noUpdatedTime = currentTime - client.getLastUpdatedTime();
            return client.isEphemeral() && (
                    isExpirePublishedClient(noUpdatedTime, client) && isExpireSubscriberClient(noUpdatedTime, client)
                            || noUpdatedTime > ClientConfig.getInstance().getClientExpiredTime());
        }
        
        private boolean isExpirePublishedClient(long noUpdatedTime, IpPortBasedClient client) {
            return client.getAllPublishedService().isEmpty() && noUpdatedTime > Constants.DEFAULT_IP_DELETE_TIMEOUT;
        }
        
        private boolean isExpireSubscriberClient(long noUpdatedTime, IpPortBasedClient client) {
            return client.getAllSubscribeService().isEmpty() || noUpdatedTime > switchDomain.getDefaultPushCacheMillis();
        }
    }
}

客户端对象(IpPortBasedClient)

//基于ip和端口的客户端比如服务提供者和发布者
public class IpPortBasedClient extends AbstractClient {
    
    public static final String ID_DELIMITER = "#";
    //格式ip:port#true
    private final String clientId;
    
    private final boolean ephemeral;
    //用于计算哪台服务器负责该client的参数
    private final String responsibleId;
    //定时检查临时节点发起的心跳
    private ClientBeatCheckTaskV2 beatCheckTask;
    //定时主动检查非临时节点比如长链接的节点[集群服务]的健康检查
    private HealthCheckTaskV2 healthCheckTaskV2;
    
    public IpPortBasedClient(String clientId, boolean ephemeral) {
        this.ephemeral = ephemeral;
        this.clientId = clientId;
        this.responsibleId = getResponsibleTagFromId();
    }
    
    //截取clientId的ip:port部分作为responsebleId
    private String getResponsibleTagFromId() {
        int index = clientId.indexOf(IpPortBasedClient.ID_DELIMITER);
        return clientId.substring(0, index);
    }
    //静态方法生成clientId address(ip:port)
    public static String getClientId(String address, boolean ephemeral) {
        return address + ID_DELIMITER + ephemeral;
    }
    
   ...
    // 客户端添加服务发布信息
    @Override
    public boolean addServiceInstance(Service service, InstancePublishInfo instancePublishInfo) {
        return super.addServiceInstance(service, 
             parseToHealthCheckInstance(instancePublishInfo));
    }

    //该方法没有发现有地方用到
    @Override
    public boolean isExpire(long currentTime) {
        return isEphemeral() && getAllPublishedService().isEmpty() 
             && currentTime - getLastUpdatedTime() > 
                ClientConfig.getInstance().getClientExpiredTime();
    }
    
    //服务的所有发布者
    public Collection<InstancePublishInfo> getAllInstancePublishInfo() {
        return publishers.values();
    }
    
    //释放资源2、取消定时任务
    @Override
    public void release() {
        super.release();
        if (ephemeral) {
            HealthCheckReactor.cancelCheck(beatCheckTask);
        } else {
            healthCheckTaskV2.setCancelled(true);
        }
    }
    
    // 包装 instancePublishInfo 对象成一个HealthCheckInstancePublishInfo  对象
    // 区别在于 后者包括所有的前者的属性之外 还包括了属性:lastHeartBeatTime[最近的心跳时间] 
    // 和 HealthCheckStatus
    // 包括最近健康检查时间 成功失败状态 及次数统计
    private HealthCheckInstancePublishInfo parseToHealthCheckInstance(InstancePublishInfo instancePublishInfo) {
        HealthCheckInstancePublishInfo result;
        if (instancePublishInfo instanceof HealthCheckInstancePublishInfo) {
            result = (HealthCheckInstancePublishInfo) instancePublishInfo;
        } else {
            result = new HealthCheckInstancePublishInfo();
            result.setIp(instancePublishInfo.getIp());
            result.setPort(instancePublishInfo.getPort());
            result.setHealthy(instancePublishInfo.isHealthy());
            result.setCluster(instancePublishInfo.getCluster());
            result.setExtendDatum(instancePublishInfo.getExtendDatum());
        }
        if (!ephemeral) {
            //持久化节点因为主动发起健康检查所以需要初始化HealthCheckStatus对象
            result.initHealthCheck();
        }
        return result;
    }
    
    /**
     * Init client.
     */
    public void init() {
        if (ephemeral) {
            //临时节点创建心跳检查任务 
            beatCheckTask = new ClientBeatCheckTaskV2(this);
            HealthCheckReactor.scheduleCheck(beatCheckTask);
        } else {
            //持久化节点创建监控检查任务 
            healthCheckTaskV2 = new HealthCheckTaskV2(this);
            HealthCheckReactor.scheduleCheck(healthCheckTaskV2);
        }
    }
    
    /**
     * Purely put instance into service without publish events.
     */
    public void putServiceInstance(Service service, InstancePublishInfo instance) {
        if (null == publishers.put(service, parseToHealthCheckInstance(instance))) {
            MetricsMonitor.incrementInstanceCount();
        }
    }
}

总结

为了让读者可以更高效的吸收知识,我尽量把篇幅都写短一点,尽量做到在轻松愉快的心情下又不占用太多时间就可以学习完一篇,因此复杂的知识点分成多个篇幅来讲解,Client 还有 ClientFacotry 工厂实现机制 和 心跳任务处理机制,下一篇我们继续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值