(Nacos源码解析二)Nacos 服务发现源码解析

Nacos源码解析系列目录

服务发现源码解析

核心API

在这里插入图片描述
Nacos 服务发现源码图
在这里插入图片描述
大家可以根据源码图,一步一步看着各节点源码解析

客户端

1、客户端获取查询实例列表

通过服务名称 获取和当前服务名称相关的服务实例
com.alibaba.nacos.client.naming.NacosNamingService#getAllInstances(java.lang.String)
com.alibaba.nacos.client.naming.NacosNamingService#getAllInstances(java.lang.String, java.lang.String, java.util.List<java.lang.String>, boolean)

    @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 {
            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;
    }

获取服务信息
com.alibaba.nacos.client.naming.core.HostReactor#getServiceInfo

public ServiceInfo getServiceInfo(final String serviceName, final String clusters) {
    
    NAMING_LOGGER.debug("failover-mode: " + failoverReactor.isFailoverSwitch());
    String key = ServiceInfo.getKey(serviceName, clusters);
    if (failoverReactor.isFailoverSwitch()) {
        return failoverReactor.getService(key);
    }
    // 获取ServiceInfo
    ServiceInfo serviceObj = getServiceInfo0(serviceName, clusters);
    // 第一次获取是空的
    if (null == serviceObj) {
        serviceObj = new ServiceInfo(serviceName, clusters);
        
        serviceInfoMap.put(serviceObj.getKey(), serviceObj);
        
        updatingMap.put(serviceName, new Object());
        // 更新服务列表
        updateServiceNow(serviceName, clusters);
        updatingMap.remove(serviceName);
        
    } else if (updatingMap.containsKey(serviceName)) {
        
        if (UPDATE_HOLD_INTERVAL > 0) {
            // hold a moment waiting for update finish
            synchronized (serviceObj) {
                try {
                    serviceObj.wait(UPDATE_HOLD_INTERVAL);
                } catch (InterruptedException e) {
                    NAMING_LOGGER
                            .error("[getServiceInfo] serviceName:" + serviceName + ", clusters:" + clusters, e);
                }
            }
        }
    }
    // 开启定时任务
    scheduleUpdateIfAbsent(serviceName, clusters);
    
    return serviceInfoMap.get(serviceObj.getKey());
}

获取客户端的服务实例缓存信息
com.alibaba.nacos.client.naming.core.HostReactor#getServiceInfo0

private ServiceInfo getServiceInfo0(String serviceName, String clusters) {
        
        String key = ServiceInfo.getKey(serviceName, clusters);
        
        return serviceInfoMap.get(key);
    }

获取服务端最新的实例,将获取的实例列表放入serviceInfoMap
com.alibaba.nacos.client.naming.core.HostReactor#updateServiceNow

  private void updateServiceNow(String serviceName, String clusters) {
        try {
            updateService(serviceName, clusters);
        } catch (NacosException e) {
            NAMING_LOGGER.error("[NA] failed to update serviceName: " + serviceName, e);
        }
    }

调用Service API 获取 实例数据并存入缓存
com.alibaba.nacos.client.naming.core.HostReactor#updateService

public void updateService(String serviceName, String clusters) throws NacosException {
    ServiceInfo oldService = getServiceInfo0(serviceName, clusters);
    try {
        // 查询实例列表API
        String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUdpPort(), false);
        
        if (StringUtils.isNotEmpty(result)) {
            // 将获取的实例列表放入serviceInfoMap
            processServiceJson(result);
        }
    } finally {
        if (oldService != null) {
            synchronized (oldService) {
                oldService.notifyAll();
            }
        }
    }
}

2、定时获取最新实例列表,更新客户端的服务缓存

com.alibaba.nacos.client.naming.core.HostReactor#scheduleUpdateIfAbsent

 public void scheduleUpdateIfAbsent(String serviceName, String clusters) {
        if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) {
            return;
        }
        
        synchronized (futureMap) {
            if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) {
                return;
            }
            // 开启schedule任务
            ScheduledFuture<?> future = addTask(new UpdateTask(serviceName, clusters));
            futureMap.put(ServiceInfo.getKey(serviceName, clusters), future);
        }
    }

定时获取服务端最新实例数据并更新到本地缓存
com.alibaba.nacos.client.naming.core.HostReactor.UpdateTask#run

@Override
public void run() {
    long delayTime = DEFAULT_DELAY;
    
    try {
        ServiceInfo serviceObj = serviceInfoMap.get(ServiceInfo.getKey(serviceName, clusters));
        
        if (serviceObj == null) {
            // 获取最新的实例列表,并更新serviceInfoMap
            updateService(serviceName, clusters);
            return;
        }
        
        if (serviceObj.getLastRefTime() <= lastRefTime) {
            updateService(serviceName, clusters);
            serviceObj = serviceInfoMap.get(ServiceInfo.getKey(serviceName, clusters));
        } else {
            // if serviceName already updated by push, we should not override it
            // since the push data may be different from pull through force push
            refreshOnly(serviceName, clusters);
        }
        
        lastRefTime = serviceObj.getLastRefTime();
        
        if (!notifier.isSubscribed(serviceName, clusters) && !futureMap
                .containsKey(ServiceInfo.getKey(serviceName, clusters))) {
            // abort the update task
            NAMING_LOGGER.info("update task is stopped, service:" + serviceName + ", clusters:" + clusters);
            return;
        }
        if (CollectionUtils.isEmpty(serviceObj.getHosts())) {
            incFailCount();
            return;
        }
        delayTime = serviceObj.getCacheMillis();
        resetFailCount();
    } catch (Throwable e) {
        incFailCount();
        NAMING_LOGGER.warn("[NA] failed to update serviceName: " + serviceName, e);
    } finally {
        // 嵌套调度
        executor.schedule(this, Math.min(delayTime << failCount, DEFAULT_DELAY * 60), TimeUnit.MILLISECONDS);
    }
}

服务端

1、客户端调用服务端获取全部实例接口

com.alibaba.nacos.naming.controllers.InstanceController#list
com.alibaba.nacos.naming.controllers.InstanceController#doSrvIpxt
从serviceMap 获取Service

 public ObjectNode doSrvIpxt(String namespaceId, String serviceName, String agent, String cluste
         int udpPort, String env, boolean isCheck, String app, String tid, boolean healthyOnly) 
     
     ClientInfo clientInfo = new ClientInfo(agent);
     ObjectNode result = JacksonUtils.createEmptyJsonNode();
     // 获取服务
 //  Map(namespace, Map(group::serviceName, Service)).
//   private final Map<String, Map<String, Service>> serviceMap = new ConcurrentHashMap<>();
//   从Nacos的serviceMap获取
     Service service = serviceManager.getService(namespaceId, serviceName);
     long cacheMillis = switchDomain.getDefaultCacheMillis();
     
     ...
     // 获取全部实例
     srvedIPs = service.srvIPs(Arrays.asList(StringUtils.split(clusters, ",")));
     ...
 }

com.alibaba.nacos.naming.core.Service#srvIPs
com.alibaba.nacos.naming.core.Service#allIPs(java.util.List<java.lang.String>)
com.alibaba.nacos.naming.core.Cluster#allIPs()

public List<Instance> allIPs() {
        List<Instance> allInstances = new ArrayList<>();
        // 持久化实例 CP架构
        // Set<Instance> persistentInstances = new HashSet<>();
        allInstances.addAll(persistentInstances);
        // 临时实例 AP架构
        // private Set<Instance> ephemeralInstances = new HashSet<>();
        allInstances.addAll(ephemeralInstances);
        return allInstances;
    }
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

janyxe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值