3. nacos之服务发现

目录

 (1)获取服务实例列表

(1.1)从naming拉取实例列表

(1.2)namingServer处理拉取服务实例列表的请求

(1.3)client接收并处理拉取到的服务实例列表结果

(1.4)定时去naming拉取服务实例列表


示例代码:

public class MainConsumer {
    private static Logger logger = LoggerFactory.getLogger(MainConsumer.class);
    public static void main(String[] args) throws NacosException {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848");
        properties.put(PropertyKeyConst.USERNAME, "nacos");
        properties.put(PropertyKeyConst.PASSWORD, "nacos");

        Instance instance = new Instance();
        instance.setIp("127.0.0.1");
        instance.setPort(800);
        instance.setWeight(2);
        Map<String, String> map = new HashMap<String, String>();
        map.put("netType", "external");
        map.put("version", "2.0");
        instance.setMetadata(map);

        NamingService namingService = NacosFactory.createNamingService(properties);
        namingService.registerInstance("nacos.service.consumer", instance);
        // 拉取服务nacos.service.provider的所有实例列表
        List<com.alibaba.nacos.api.naming.pojo.Instance> allInstances = namingService.getAllInstances("nacos.service.provider");
        logger.info("allInstances:{}", allInstances);

        while (true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

 (1)获取服务实例列表

public List<Instance> getAllInstances(String serviceName) throws NacosException {
        // 传递空的cluster列表
        return getAllInstances(serviceName, new ArrayList<String>());
    }

public List<Instance> getAllInstances(String serviceName, List<String> clusters) throws NacosException {
        // 传递subscribe参数为true
        // 代表订阅服务serviceName
        return getAllInstances(serviceName, clusters, true);
    }

public List<Instance> getAllInstances(String serviceName, String groupName, List<String> clusters,
            boolean subscribe) throws NacosException {

        ServiceInfo serviceInfo;
        if (subscribe) {
            logger.info("获取服务[{}]的所有服务实例列表,并且订阅", serviceName);
            serviceInfo = hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName),
                    StringUtils.join(clusters, ","));
        } else {
            logger.info("获取服务[{}]的所有服务实例列表,但不订阅", serviceName);
            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;
    }
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);
        }

        // 从serviceInfoMap缓存中获取
        ServiceInfo serviceObj = getServiceInfo0(serviceName, clusters);

        if (null == serviceObj) {
            // 从serviceInfoMap中未读物到serviceInfo
            // 先构造一个serviceInfo保存到serviceInfoMap中,然后去服务端拉取
            serviceObj = new ServiceInfo(serviceName, clusters);

            serviceInfoMap.put(serviceObj.getKey(), serviceObj);
            // 在updatingMap中保存更新记录
            updatingMap.put(serviceName, new Object());
            // 从naming中获取服务信息
            updateServiceNow(serviceName, clusters);
            // 移除updatingMap中的更新记录
            updatingMap.remove(serviceName);

        } else if (updatingMap.containsKey(serviceName)) {
            // serviceName正在更新...
            // 然后默认wait(5s)
            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);
                    }
                }
            }
        }
        // 添加定时任务,定时去拉取serviceName的信息,默认1s一次
        scheduleUpdateIfAbsent(serviceName, clusters);
        // 从缓存中读取
        return serviceInfoMap.get(serviceObj.getKey());
    }

抛开故障转移机制不分析。

先尝试从缓存中获取,获取不到就会立即构造一个service对象,然后立即从namingServer中获取。接着就会提交一个任务,去定时拉取更新实例列表

(1.1)从naming拉取实例列表

private void updateServiceNow(String serviceName, String clusters) {
        try {
            updateService(serviceName, clusters);
        } catch (NacosException e) {
            NAMING_LOGGER.error("[NA] failed to update serviceName: " + serviceName, e);
        }
    }
public void updateService(String serviceName, String clusters) throws NacosException {
        // 从缓存中读取服务实例列表
        ServiceInfo oldService = getServiceInfo0(serviceName, clusters);
        try {
            // 发送拉取服务实例列表请求,并且传递了参数,healthOnly=false, 代表不只拉取健康的服务实例
            String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUdpPort(), false);

            if (StringUtils.isNotEmpty(result)) {
                processServiceJson(result);
            }
        } finally {
            // 通过oldService去唤醒前面wait的线程
            if (oldService != null) {
                synchronized (oldService) {
                    oldService.notifyAll();
                }
            }
        }
    }

发送拉取请求

public String queryList(String serviceName, String clusters, int udpPort, boolean healthyOnly)
            throws NacosException {

        final Map<String, String> params = new HashMap<String, String>(8);
        params.put(CommonParams.NAMESPACE_ID, namespaceId);
        params.put(CommonParams.SERVICE_NAME, serviceName);
        params.put("clusters", clusters);
        params.put("udpPort", String.valueOf(udpPort));
        params.put("clientIP", NetUtils.localIP());
        params.put("healthyOnly", String.valueOf(healthyOnly));
        logger.info("拉取服务实例列表[{}], 本地clientIp:{}, udpPort:{}", serviceName, params.get("clientIp"), params.get("udpPort"));
        return reqApi(UtilAndComs.nacosUrlBase + "/instance/list", params, HttpMethod.GET);
    }

请求的url:/nacos/v1/ns/instance/list   get请求,

这里需要注意的是,请求参数中,有两个参数udpPort, clientIp, 联想上一节(2. nacos之服务注册_ZXH240651200的博客-CSDN博客)中的udp发送服务变化通知,好像有联系。

接着分析namingServer如何处理这个请求。

(1.2)namingServer处理拉取服务实例列表的请求

上面分析了get请求url:/nacos/v1/ns/instance/list

com.alibaba.nacos.naming.controllers.InstanceController#list

   /**
     * Get all instance of input service.
     *
     * @param request http request
     * @return list of instance
     * @throws Exception any error during list
     */
    @GetMapping("/list")
    @Secured(parser = NamingResourceParser.class, action = ActionTypes.READ)
    public ObjectNode list(HttpServletRequest request) throws Exception {

        String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
        String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
        NamingUtils.checkServiceNameFormat(serviceName);
        // 客户端agent,对于java客户端来说,是客户端的版本号:
        // 在客户端reqApi中构建header的时候,指定的
        //  header.addParam(HttpHeaderConsts.USER_AGENT_HEADER, UtilAndComs.VERSION);
        // Nacos-Java-Client:v1.4.2
        String agent = WebUtils.getUserAgent(request);
        String clusters = WebUtils.optional(request, "clusters", StringUtils.EMPTY);
        // 提取clientIp和udpPort
        String clientIP = WebUtils.optional(request, "clientIP", StringUtils.EMPTY);
        int udpPort = Integer.parseInt(WebUtils.optional(request, "udpPort", "0"));
        // 默认是""
        String env = WebUtils.optional(request, "env", StringUtils.EMPTY);
        // 默认是false
        boolean isCheck = Boolean.parseBoolean(WebUtils.optional(request, "isCheck", "false"));
        // 默认是unknow
        String app = WebUtils.optional(request, "app", StringUtils.EMPTY);
        // 默认是""
        String tenant = WebUtils.optional(request, "tid", StringUtils.EMPTY);
        // 默认是false
        boolean healthyOnly = Boolean.parseBoolean(WebUtils.optional(request, "healthyOnly", "false"));
        logger.info("拉取服务[{}] 详细信息", serviceName);
        return doSrvIpxt(namespaceId, serviceName, agent, clusters, clientIP, udpPort, env, isCheck, app, tenant,
                healthyOnly);
    }
public ObjectNode doSrvIpxt(String namespaceId, String serviceName, String agent, String clusters, String clientIP,
            int udpPort, String env, boolean isCheck, String app, String tid, boolean healthyOnly) throws Exception {
        // java客户端来说,根据版本号构建clientInfo
        ClientInfo clientInfo = new ClientInfo(agent);
        ObjectNode result = JacksonUtils.createEmptyJsonNode();
        // 通过namespaceId + serviceName从缓存中获取
        Service service = serviceManager.getService(namespaceId, serviceName);
        // 缓存时间,默认3s
        long cacheMillis = switchDomain.getDefaultCacheMillis();

        // now try to enable the push
        try {
            // udpPort >0 && 并且可以进行推送,java客户端,可以推送
            if (udpPort > 0 && pushService.canEnablePush(agent)) {

                // pushService保存client信息
                pushService
                        .addClient(namespaceId, serviceName, clusters, agent, new InetSocketAddress(clientIP, udpPort),
                                pushDataSource, tid, app);
                // 修改缓存时间,默认的推送缓存时间,默认10s
                cacheMillis = switchDomain.getPushCacheMillis(serviceName);
            }
        } catch (Exception e) {
            Loggers.SRV_LOG
                    .error("[NACOS-API] failed to added push client {}, {}:{}", clientInfo, clientIP, udpPort, e);
            cacheMillis = switchDomain.getDefaultCacheMillis();
        }

        // service不存在,返回空集合
        if (service == null) {
            if (Loggers.SRV_LOG.isDebugEnabled()) {
                Loggers.SRV_LOG.debug("no instance to serve for service: {}", serviceName);
            }
            result.put("name", serviceName);
            result.put("clusters", clusters);
            result.put("cacheMillis", cacheMillis);
            result.replace("hosts", JacksonUtils.createEmptyArrayNode());
            return result;
        }
        // 检查服务是否可用,不可用会抛出异常
        checkIfDisabled(service);

        List<Instance> srvedIPs;

        // 获取客户端入参clusters 所有的实例ip,
        // 如果clusters为空,就是所有实例的ip
        srvedIPs = service.srvIPs(Arrays.asList(StringUtils.split(clusters, ",")));

        // filter ips using selector:
        // 通过selector过滤ips
        // 默认是NoneSelecor,返回所有的srvedIPs
        if (service.getSelector() != null && StringUtils.isNotBlank(clientIP)) {
            srvedIPs = service.getSelector().select(clientIP, srvedIPs);
        }
        // 经过selector过滤之后,没有实例,构造返回结果
        if (CollectionUtils.isEmpty(srvedIPs)) {

            if (Loggers.SRV_LOG.isDebugEnabled()) {
                Loggers.SRV_LOG.debug("no instance to serve for service: {}", serviceName);
            }

            if (clientInfo.type == ClientInfo.ClientType.JAVA
                    && clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) {
                result.put("dom", serviceName);
            } else {
                result.put("dom", NamingUtils.getServiceName(serviceName));
            }

            result.put("name", serviceName);
            result.put("cacheMillis", cacheMillis);
            result.put("lastRefTime", System.currentTimeMillis());
            result.put("checksum", service.getChecksum());
            result.put("useSpecifiedURL", false);
            result.put("clusters", clusters);
            result.put("env", env);
            result.set("hosts", JacksonUtils.createEmptyArrayNode());
            result.set("metadata", JacksonUtils.transferToJsonNode(service.getMetadata()));
            return result;
        }

        // 构建map, key是健康度,value是相应的实例列表
        Map<Boolean, List<Instance>> ipMap = new HashMap<>(2);
        ipMap.put(Boolean.TRUE, new ArrayList<>());
        ipMap.put(Boolean.FALSE, new ArrayList<>());

        for (Instance ip : srvedIPs) {
            ipMap.get(ip.isHealthy()).add(ip);
        }

        if (isCheck) {
            result.put("reachProtectThreshold", false);
        }

        // 服务保护阈值
        double threshold = service.getProtectThreshold();

        if ((float) ipMap.get(Boolean.TRUE).size() / srvedIPs.size() <= threshold) {
            // 触发服务保护阈值
            Loggers.SRV_LOG.warn("protect threshold reached, return all ips, service: {}", serviceName);
            if (isCheck) {
                result.put("reachProtectThreshold", true);
            }
            // 不区分服务健康度,都是健康的服务
            ipMap.get(Boolean.TRUE).addAll(ipMap.get(Boolean.FALSE));
            ipMap.get(Boolean.FALSE).clear();
        }

        // 如果有检查标志,就直接返回了
        // 默认是false
        if (isCheck) {
            result.put("protectThreshold", service.getProtectThreshold());
            result.put("reachLocalSiteCallThreshold", false);

            return JacksonUtils.createEmptyJsonNode();
        }

        ArrayNode hosts = JacksonUtils.createEmptyArrayNode();

        for (Map.Entry<Boolean, List<Instance>> entry : ipMap.entrySet()) {
            List<Instance> ips = entry.getValue();

            // 如果只返回健康的服务,就跳过不健康的服务
            if (healthyOnly && !entry.getKey()) {
                continue;
            }

            // 构造返回结果
            for (Instance instance : ips) {

                // remove disabled instance:
                // 剔除不可用的服务
                if (!instance.isEnabled()) {
                    continue;
                }

                ObjectNode ipObj = JacksonUtils.createEmptyJsonNode();

                ipObj.put("ip", instance.getIp());
                ipObj.put("port", instance.getPort());
                // deprecated since nacos 1.0.0:
                ipObj.put("valid", entry.getKey());
                ipObj.put("healthy", entry.getKey());
                ipObj.put("marked", instance.isMarked());
                ipObj.put("instanceId", instance.getInstanceId());
                ipObj.set("metadata", JacksonUtils.transferToJsonNode(instance.getMetadata()));
                ipObj.put("enabled", instance.isEnabled());
                ipObj.put("weight", instance.getWeight());
                ipObj.put("clusterName", instance.getClusterName());
                if (clientInfo.type == ClientInfo.ClientType.JAVA
                        && clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) {
                    ipObj.put("serviceName", instance.getServiceName());
                } else {
                    ipObj.put("serviceName", NamingUtils.getServiceName(instance.getServiceName()));
                }

                ipObj.put("ephemeral", instance.isEphemeral());
                hosts.add(ipObj);

            }
        }

        result.replace("hosts", hosts);
        if (clientInfo.type == ClientInfo.ClientType.JAVA
                && clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) {
            result.put("dom", serviceName);
        } else {
            result.put("dom", NamingUtils.getServiceName(serviceName));
        }
        result.put("name", serviceName);
        result.put("cacheMillis", cacheMillis);
        result.put("lastRefTime", System.currentTimeMillis());
        result.put("checksum", service.getChecksum());
        result.put("useSpecifiedURL", false);
        result.put("clusters", clusters);
        result.put("env", env);
        result.replace("metadata", JacksonUtils.transferToJsonNode(service.getMetadata()));
        return result;
    }

这里将客户端的udpport和clientIp信息,构造成pushclient并保存到pushService中,

客户端如果传递了healthyOnly=true,那么值返回健康的实例

如果配置了阈值保护,且触发了阈值保护:ipMap.get(Boolean.TRUE).size() / srvedIPs.size() <= threshold, 那么认为所有服务实例都是健康的,返回给客户端。

并且给客户端返回了cacheMillis,默认是3s,如果有udpport和clientIp信息,就调整为10s,这个字段的作用,在下面会分析。

回到上面的pushClient地方

public void addClient(String namespaceId, String serviceName, String clusters, String agent,
            InetSocketAddress socketAddr, DataSource dataSource, String tenant, String app) {
        // 构造PushClient
        PushClient client = new PushClient(namespaceId, serviceName, clusters, agent, socketAddr, dataSource, tenant,
                app);
        // 添加publicClient
        addClient(client);
    }
 public void addClient(PushClient client) {
        // client is stored by key 'serviceName' because notify event is driven by serviceName change
        // 构造namespaceId+serviceName
        String serviceKey = UtilsAndCommons.assembleFullServiceName(client.getNamespaceId(), client.getServiceName());
        ConcurrentMap<String, PushClient> clients = clientMap.get(serviceKey);
        if (clients == null) {
            clientMap.putIfAbsent(serviceKey, new ConcurrentHashMap<>(1024));
            clients = clientMap.get(serviceKey);
        }
        // 如果pushclient已经存在了,就refresh()更新一下lastRefTime时间
        // 这个lastRefTime的作用是,在进行发送通知消息的时候,如果lastRefTime超过10s未更新,就不会发送udp通知
        PushClient oldClient = clients.get(client.toString());
        if (oldClient != null) {
            oldClient.refresh();
        } else {
            PushClient res = clients.putIfAbsent(client.toString(), client);
            if (res != null) {
                Loggers.PUSH.warn("client: {} already associated with key {}", res.getAddrStr(), res.toString());
            }
            Loggers.PUSH.debug("client: {} added for serviceName: {}", client.getAddrStr(), client.getServiceName());
        }
    }

这里就形成闭环了,服务请求其他服务实例列表的时候,带上了自己的clientIp+udpPort, 然后namingServer把clientIp+udpport构造成pushClient,当关心的服务发生变更时,就通过pushClient进行反向通知

(1.3)client接收并处理拉取到的服务实例列表结果

回到1.1部分,发送拉取服务实例请求后的部分,

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

public void updateService(String serviceName, String clusters) throws NacosException {
        // 从缓存中读取服务实例列表
        ServiceInfo oldService = getServiceInfo0(serviceName, clusters);
        try {
            // 发送拉取服务实例列表请求,并且传递了参数,healthOnly=false, 代表不只拉取健康的服务实例
            String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUdpPort(), false);
            // 处理拉取到的结果
            if (StringUtils.isNotEmpty(result)) {
                processServiceJson(result);
            }
        } finally {
            // 通过oldService去唤醒前面wait的线程
            if (oldService != null) {
                synchronized (oldService) {
                    oldService.notifyAll();
                }
            }
        }
    }
public ServiceInfo processServiceJson(String json) {
        ServiceInfo serviceInfo = JacksonUtils.toObj(json, ServiceInfo.class);
        String serviceKey = serviceInfo.getKey();
        if (serviceKey == null) {
            return null;
        }
        // 先从缓存中读取旧的缓存
        ServiceInfo oldService = serviceInfoMap.get(serviceKey);
        // 如果开启了pushEnmpty保护,并且拉取的serviceInfo不可用,就返回旧的缓存
        if (pushEmptyProtection && !serviceInfo.validate()) {
            //empty or error push, just ignore
            return oldService;
        }

        // 默认没有发生变化
        boolean changed = false;

        if (oldService != null) {

            // 判断刷新时间
            if (oldService.getLastRefTime() > serviceInfo.getLastRefTime()) {
                NAMING_LOGGER.warn("out of date data received, old-t: " + oldService.getLastRefTime() + ", new-t: "
                        + serviceInfo.getLastRefTime());
            }
            // 将拉取到的serviceInfo保存到缓存中
            serviceInfoMap.put(serviceInfo.getKey(), serviceInfo);

            // 将旧的缓存hosts构造成map
            Map<String, Instance> oldHostMap = new HashMap<String, Instance>(oldService.getHosts().size());
            for (Instance host : oldService.getHosts()) {
                oldHostMap.put(host.toInetAddr(), host);
            }

            // 将新的服务列表hosts,构造成map
            Map<String, Instance> newHostMap = new HashMap<String, Instance>(serviceInfo.getHosts().size());
            for (Instance host : serviceInfo.getHosts()) {
                newHostMap.put(host.toInetAddr(), host);
            }
            //发生变化的instances
            Set<Instance> modHosts = new HashSet<Instance>();
            // 新的instances
            Set<Instance> newHosts = new HashSet<Instance>();
            // 删除的instances
            Set<Instance> remvHosts = new HashSet<Instance>();

            List<Map.Entry<String, Instance>> newServiceHosts = new ArrayList<Map.Entry<String, Instance>>(
                    newHostMap.entrySet());
            for (Map.Entry<String, Instance> entry : newServiceHosts) {
                Instance host = entry.getValue();
                String key = entry.getKey();
                // host属性发生变化
                if (oldHostMap.containsKey(key) && !StringUtils
                        .equals(host.toString(), oldHostMap.get(key).toString())) {
                    modHosts.add(host);
                    continue;
                }
                // oldHost里面有的,就认为是新增的host
                if (!oldHostMap.containsKey(key)) {
                    newHosts.add(host);
                }
            }

            for (Map.Entry<String, Instance> entry : oldHostMap.entrySet()) {
                Instance host = entry.getValue();
                String key = entry.getKey();
                if (newHostMap.containsKey(key)) {
                    continue;
                }
                // newHosts里面不包含的,认为是删除的host
                if (!newHostMap.containsKey(key)) {
                    remvHosts.add(host);
                }

            }

            if (newHosts.size() > 0) {
                changed = true;
                NAMING_LOGGER.info("new ips(" + newHosts.size() + ") service: " + serviceInfo.getKey() + " -> "
                        + JacksonUtils.toJson(newHosts));
            }

            if (remvHosts.size() > 0) {
                changed = true;
                NAMING_LOGGER.info("removed ips(" + remvHosts.size() + ") service: " + serviceInfo.getKey() + " -> "
                        + JacksonUtils.toJson(remvHosts));
            }

            if (modHosts.size() > 0) {
                changed = true;
                // 针对属性变化的host,更新beat信息
                updateBeatInfo(modHosts);
                NAMING_LOGGER.info("modified ips(" + modHosts.size() + ") service: " + serviceInfo.getKey() + " -> "
                        + JacksonUtils.toJson(modHosts));
            }
            // 保存原始json
            serviceInfo.setJsonFromServer(json);

            if (newHosts.size() > 0 || remvHosts.size() > 0 || modHosts.size() > 0) {
                // 发生变化,就publishEvent-->InstancesChangeEvent
                NotifyCenter.publishEvent(new InstancesChangeEvent(serviceInfo.getName(), serviceInfo.getGroupName(),
                        serviceInfo.getClusters(), serviceInfo.getHosts()));
                DiskCache.write(serviceInfo, cacheDir);
            }

        } else {
            //oldServices == null
            changed = true;
            NAMING_LOGGER.info("init new ips(" + serviceInfo.ipCount() + ") service: " + serviceInfo.getKey() + " -> "
                    + JacksonUtils.toJson(serviceInfo.getHosts()));
            // 保存到缓存
            serviceInfoMap.put(serviceInfo.getKey(), serviceInfo);
            // 发送事件
            NotifyCenter.publishEvent(new InstancesChangeEvent(serviceInfo.getName(), serviceInfo.getGroupName(),
                    serviceInfo.getClusters(), serviceInfo.getHosts()));
            serviceInfo.setJsonFromServer(json);
            DiskCache.write(serviceInfo, cacheDir);
        }

        MetricsMonitor.getServiceInfoMapSizeMonitor().set(serviceInfoMap.size());

        if (changed) {
            NAMING_LOGGER.info("current ips:(" + serviceInfo.ipCount() + ") service: " + serviceInfo.getKey() + " -> "
                    + JacksonUtils.toJson(serviceInfo.getHosts()));
        }

        return serviceInfo;
    }

得到结果之后,会分析出新增的instance,删除的instance,和属性发生变化的instance(会更新beat信息),如果整体发生了变化,会publishEvent(InstancesChangeEvent)。

(1.4)定时去naming拉取服务实例列表

在(1)中有个定时任务

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

// 添加定时任务,定时去拉取serviceName的信息,默认1s一次
scheduleUpdateIfAbsent(serviceName, clusters);

    public void scheduleUpdateIfAbsent(String serviceName, String clusters) {
        // futureMap中已经有任务了,就不在添加了
        if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) {
            return;
        }
        
        synchronized (futureMap) {
            // 再次判断
            if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) {
                return;
            }
            // 添加任务
            ScheduledFuture<?> future = addTask(new UpdateTask(serviceName, clusters));
            // 将future保存到futureMap中
            futureMap.put(ServiceInfo.getKey(serviceName, clusters), future);
        }
    }

 添加的UpdateTask

com.alibaba.nacos.client.naming.core.HostReactor.UpdateTask#run

public void run() {
            //默认1s
            long delayTime = DEFAULT_DELAY;

            try {
                // 获取服务信息缓存
                ServiceInfo serviceObj = serviceInfoMap.get(ServiceInfo.getKey(serviceName, clusters));
                // 获取为空,会立即拉取
                if (serviceObj == null) {
                    updateService(serviceName, clusters);
                    return;
                }
                // 缓存的serviceInfo小于最后刷新时间(初始化为Long.MAX_VALUE),就立即拉取
                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
                    // 缓存信息已经是新的了,仅仅是刷新一下服务端的lastRefTime
                    refreshOnly(serviceName, clusters);
                }

                lastRefTime = serviceObj.getLastRefTime();
                // 没有订阅本地事件,并且futureMap没有包含这个服务的key
                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;
                }
                // 如果service的实例列表为空,就增加failCount
                if (CollectionUtils.isEmpty(serviceObj.getHosts())) {
                    incFailCount();
                    return;
                }
                // 获取naming返回的cacheMillis
                delayTime = serviceObj.getCacheMillis();
                // 充值failCount
                resetFailCount();
            } catch (Throwable e) {
                incFailCount();
                NAMING_LOGGER.warn("[NA] failed to update serviceName: " + serviceName, e);
            } finally {
                // 提交下一个事件,延时时间是min(delayTime << failCount, 60s)
                // 有pushclient信息时,delayTime = 10s
                // 无pushclient信息时,delayTime = 3s
                // 正常情况下,failCount = 0
                // 当发生异常情况是,failCount会递增,但是不会大于6
                executor.schedule(this, Math.min(delayTime << failCount, DEFAULT_DELAY * 60), TimeUnit.MILLISECONDS);
            }
        }

每次updateTask执行完成之后,会将自己再次提交到线程池,延时时间做适当调整,主要根据naming返回的cacheMillis和本地的failCount,但是最终的延时时间不会大于60s.

在不出现异常,拉取的serviceObj.getHosts不为空的情况下,有push通知时,延迟为 10s;没有push通知是,延迟为1s。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值