Nacos源码学习系列第5篇服务注册核心类NacosNamingService解析

285 阅读5分钟

 NacosNamingService 是跟服务相关的核心管理类,提供了服务的上下线、服务实例查询、根据健康状态查询实例列表、根据随机权重算法查询单个健康实例、服务监听器订阅\取消订阅、分页获取服务列表。

该类还在初始化时创建了另外3个分支核心类: 客户端心跳管理类BeatReactor、服务列表管理类HostReactor、服务事件管理类EventDispatcher、NacosServer 通信代理类NamingProxy。

目录

NacosNamingService 流程图

NacosNamingService 功能概览

属性

初始化方法

核心方法

 总结

NacosNamingService 流程图

NacosNamingService 功能概览

属性

     // 服务所属命名空间
    private String namespace;

    //可以通过这个拉取serverList 区别于静态配置的serverList属性    
    private String endpoint;
    
    //服务列表以逗号隔开
    private String serverList;
    
    //服务列表的缓存和failover的目录
    private String cacheDir;

    private String logName;

    //负责本地的服务列表管理
    private HostReactor hostReactor;

    //负责心跳任务管理
    private BeatReactor beatReactor;

    //负责服务变更事件的监听器管理
    private EventDispatcher eventDispatcher;

    //负责跟nacos server 的接口通信
    private NamingProxy serverProxy;

初始化方法

 //serverList: 以逗号隔开的服务端地址
 public NacosNamingService(String serverList) {
        Properties properties = new Properties();
        properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverList);

        init(properties);
    }

    //根据配置文件中的属性配置初始化 NacosNamingService 的字段属性
    public NacosNamingService(Properties properties) {
        init(properties);
    }

    private void init(Properties properties) {
        // 首先从系统属性中获取namespace 属性值,如果没有找到从配置文件找namespace 属性
        // 否则默认是public
        namespace = InitUtils.initNamespaceForNaming(properties);
        //把属性值[spring.cloud.nacos.discovery.server-addr]赋给serverList
        initServerAddr(properties);

        InitUtils.initWebRootContext();
        //从系统属性[com.alibaba.nacos.naming.cache.dir]获取 缓存目录
        initCacheDir();

        initLogName(properties);
        
        //服务变更事件的监听器管理类
        eventDispatcher = new EventDispatcher();
        
        // 远程Ncos服务服务的代理类 所有跟远程Nacos服务接口通讯的统一入口
        serverProxy = new NamingProxy(namespace, endpoint, serverList);
        serverProxy.setProperties(properties);

        //创建管理心跳信息的核心类
        beatReactor = new BeatReactor(serverProxy, 
                 initClientBeatThreadCount(properties));

        //创建 维护和更新本地服务列表、同远程服务器保持同步的 核心类
        hostReactor = new HostReactor(eventDispatcher, serverProxy, cacheDir, 
                 isLoadCacheAtStart(properties), initPollingThreadCount(properties));
    }
  
       //执行心跳任务的线程数  默认是当前服务的核数/2
     private int initClientBeatThreadCount(Properties properties) {
       ...
        return NumberUtils.toInt(properties.getProperty(
                   PropertyKeyConst.NAMING_CLIENT_BEAT_THREAD_COUNT),
            UtilAndComs.DEFAULT_CLIENT_BEAT_THREAD_COUNT);
       }

    //执行服务类别更新处理的线程数  默认是当前服务的核数/2
    private int initPollingThreadCount(Properties properties) {
        ...
        return NumberUtils.toInt(properties.getProperty(
                   PropertyKeyConst.NAMING_POLLING_THREAD_COUNT),
            UtilAndComs.DEFAULT_POLLING_THREAD_COUNT);
    }

   //执行心跳任务的线程数  默认是当前服务的核数/2
   private int initClientBeatThreadCount(Properties properties) {
        if (properties == null) {
            return UtilAndComs.DEFAULT_CLIENT_BEAT_THREAD_COUNT;
        }

        return NumberUtils.toInt(properties.getProperty(
            PropertyKeyConst.NAMING_CLIENT_BEAT_THREAD_COUNT),
            UtilAndComs.DEFAULT_CLIENT_BEAT_THREAD_COUNT);
        }

 

    // 设置 loadCacheAtStart 属性 
    private boolean isLoadCacheAtStart(Properties properties) {
        boolean loadCacheAtStart = false;
        if (properties != null && 
              StringUtils.isNotEmpty(properties.getProperty(
                 PropertyKeyConst.NAMING_LOAD_CACHE_AT_START))) {
                   loadCacheAtStart = BooleanUtils.toBoolean(
                   properties.getProperty(PropertyKeyConst.NAMING_LOAD_CACHE_AT_START));
           }
        return loadCacheAtStart;
    }
    
    //初始化 endpoint  和 serverList  [从配置属性获取]
    private void initServerAddr(Properties properties) {
        serverList = properties.getProperty(PropertyKeyConst.SERVER_ADDR);
        endpoint = InitUtils.initEndpoint(properties);
        if (StringUtils.isNotEmpty(endpoint)) {
            serverList = "";
        }
    }

    //初始化缓存目录
    private void initCacheDir() {
        cacheDir = System.getProperty("com.alibaba.nacos.naming.cache.dir");
        if (StringUtils.isEmpty(cacheDir)) {
            cacheDir = System.getProperty("user.home") + "/nacos/naming/" + namespace;
        }
    }

initClientBeatThreadCount: 执行心跳任务的线程数,建议配置该属性的值,因为如果不配置的话默认是cpu 逻辑核/2 如果是8C的机器就会创建4个核心线程, 实际上发送心跳的任务是5s一次所以该属性设置为1足够了。

initPollingThreadCount 服务定时更新的线程数,也建议配置该属性的值,因为如果不配置的话默认是cpu 逻辑核/2 如果是8C的机器就会创建4个核心线程, 如果微服务的服务数不多而且变更不是特别频繁的情况下 这个属性可以设置小一些。个人觉得一般设置成1是够了。因为即便1个线程用完了,线程池还会自动扩容。

那么这2个属性在哪里设置呢?我目前还么有知道,如果有读者找到了,麻烦评论区回复下。

核心方法

    ....
    //服务注册
    @Override
    public void registerInstance(String serviceName, String groupName, Instance 
       instance) throws NacosException {

        if (instance.isEphemeral()) {
            //创建心跳对象
            BeatInfo beatInfo = new BeatInfo();
            beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName));
            beatInfo.setIp(instance.getIp());
            beatInfo.setPort(instance.getPort());
            beatInfo.setCluster(instance.getClusterName());
            beatInfo.setWeight(instance.getWeight());
            beatInfo.setMetadata(instance.getMetadata());
            beatInfo.setScheduled(false);
            // 默认5秒
            long instanceInterval = instance.getInstanceHeartBeatInterval();
            beatInfo.setPeriod(instanceInterval == 0 ? DEFAULT_HEART_BEAT_INTERVAL : 
                    instanceInterval);

            //把心跳对象交给 beatReactor 管理 key 为groupname@@serviceName
            beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), 
                    beatInfo);
        }
        //serverProxy 完成跟远程服务器的注册请求
        serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), 
                    groupName, instance);
    }

    //服务下线
    @Override
    public void deregisterInstance(String serviceName, String groupName, Instance 
                instance) throws NacosException {
        //如果是临时节点 删除心跳任务
        if (instance.isEphemeral()) {
            beatReactor.removeBeatInfo(NamingUtils.getGroupedName(serviceName, 
               groupName), instance.getIp(), instance.getPort());
        }
        serverProxy.deregisterService(NamingUtils.getGroupedName(serviceName, 
               groupName), instance);
    }


   //查询服务下的实例列表
    @Override
    public List<Instance> getAllInstances(String serviceName, String groupName, 
                 List<String> clusters, boolean subscribe) throws NacosException {

        ServiceInfo serviceInfo;
        // 如果subscribe 为true 从本地获取[本地么有再从远程拉取]
        if (subscribe) {
            //优先从本地列表获取 
            serviceInfo = 
               hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, 
               groupName), StringUtils.join(clusters, ","));
        } else {
            // 如果subscribe 为false 直接从从远程拉取    
            serviceInfo = 
               hostReactor.getServiceInfoDirectlyFromServer(
               NamingUtils.getGroupedName(serviceName, groupName), 
               StringUtils.join(clusters, ","));
        }
        List<Instance> list;
        if (serviceInfo == null || CollectionUtils.isEmpty(list = 
               serviceInfo.getHosts())) {
            return new ArrayList<Instance>();
        }
        return list;
    }

    //根据  healthy 状态筛选实例
    @Override
    public List<Instance> selectInstances(String serviceName, String groupName, 
             List<String> clusters, boolean healthy, boolean subscribe) throws 
               NacosException {
        ServiceInfo serviceInfo;
        if (subscribe) {
            serviceInfo = 
                  hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, 
                  groupName), StringUtils.join(clusters, ","));
        } else {
            serviceInfo = 
            hostReactor.getServiceInfoDirectlyFromServer(
            NamingUtils.getGroupedName(serviceName, groupName), 
            StringUtils.join(clusters, ","));
        }
        return selectInstances(serviceInfo, healthy);
    }

  

    @Override
    public Instance selectOneHealthyInstance(String serviceName, String groupName, 
             List<String> clusters, boolean subscribe) throws NacosException {
        // 根据随机权重算法选择一个健康的节点
        if (subscribe) {
            return Balancer.RandomByWeight.selectHost(
                hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, 
                groupName), StringUtils.join(clusters, ",")));
        } else {
            return Balancer.RandomByWeight.selectHost(            
                hostReactor.getServiceInfoDirectlyFromServer(
                NamingUtils.getGroupedName(serviceName, groupName), 
                StringUtils.join(clusters, ",")));
        }
    }

    //服务订阅监听器
    @Override
    public void subscribe(String serviceName, String groupName, List<String> clusters, 
              EventListener listener) throws NacosException {
               eventDispatcher.addListener(
                    hostReactor.getServiceInfo(
                    NamingUtils.getGroupedName(serviceName, groupName),
                    StringUtils.join(clusters, ",")), 
                    StringUtils.join(clusters, ","), listener);
    }

    //服务取消订阅监听器  
    @Override
    public void unsubscribe(String serviceName, String groupName, List<String> clusters, 
                EventListener listener) throws NacosException {
                eventDispatcher.removeListener(NamingUtils.getGroupedName(serviceName, 
                groupName), StringUtils.join(clusters, ","), listener);
    }

    //根据 groupName 和  selector 查询service 名称列表
    @Override
    public ListView<String> getServicesOfServer(int pageNo, int pageSize, String 
                groupName, AbstractSelector selector) throws NacosException {
        return serverProxy.getServiceList(pageNo, pageSize, groupName, selector);
    }

    //查询所有被订阅监听器的服务
    @Override
    public List<ServiceInfo> getSubscribeServices() {
        return eventDispatcher.getSubscribeServices();
    }
   
    //查询服务器的涨停
    @Override
    public String getServerStatus() {
        return serverProxy.serverHealthy() ? "UP" : "DOWN";
    }
    
    //筛选健康 可用 权重 > 的实例
    private List<Instance> selectInstances(ServiceInfo serviceInfo, boolean healthy) {
        List<Instance> list;
        if (serviceInfo == null || CollectionUtils.isEmpty(list = 
                serviceInfo.getHosts())) {
            return new ArrayList<Instance>();
        }

        Iterator<Instance> iterator = list.iterator();
        while (iterator.hasNext()) {
            Instance instance = iterator.next();
            if (healthy != instance.isHealthy() || !instance.isEnabled() || 
                  instance.getWeight() <= 0) {
                iterator.remove();
            }
        }

        return list;
    }
上面的代码已经筛选掉了上层的重载方法
Balancer.RandomByWeight.selectHost 随机权重 算法逻辑大家可以网上找下,这里不赘述

 总结

本章介绍了nacos 客户端 最核心的类 NacosNamingService, 作为该类的分支,接下来的章节会单独介绍BeatReactor、HostReactor、NamingProxy、EventDispatcher