[Nacos] Nacos Server处理注册请求 (六)

news2024/11/17 1:52:57

文章目录

  • InstanceController.register()
    • 1.获取到请求中指定属性的值
    • 2.通过请求参数组装出instance
    • 3.将instance写入到注册表
      • 3.1 创建一个空的service临时的实例
        • 3.1.1 重写计算校验和
        • 3.1.2 将service写入到注册表
            • 3.1.2.1 将service写入注册表
            • 3.1.2.2 初始化service内部健康检测任务
            • 3.1.2.2.1 定时清除过期instance任务
            • 3.1.2.2.2 cluster的健康检测任务
        • 3.1.3 持久实例到其他Nacos Server
      • 3.2 将instance写入到service
    • 4.总结

InstanceController.register()

在这里插入图片描述

InstanceController#register(): 处理Nacos Client的注册请求。

    @CanDistro
    @PostMapping
    @Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
    public String register(HttpServletRequest request) throws Exception {

        // 从请求中获取指定属性的值
        final String namespaceId = WebUtils
                .optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
        // 从请求中获取指定属性的值
        final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
        // 检测serviceName名称是否合法
        checkServiceNameFormat(serviceName);
        // 通过请求参数组装出instance
        final Instance instance = parseInstance(request);
        // 将instance写入到注册表
        serviceManager.registerInstance(namespaceId, serviceName, instance);
        return "ok";
    }
  • 从请求中获取指定属性的值, namespaceId和serviceName, 然后检查serviceName名称是否合法
  • 通过请求参数组装出instance
  • 最后将instance写入注册表中

1.获取到请求中指定属性的值

获取到请求中指定属性namespaceId和serviceName

在这里插入图片描述

WebUtils#optional()

    public static String optional(final HttpServletRequest req, final String key, final String defaultValue) {
        // 若请求map中不包含指定属性的值,或其值为null,则直接返回给定的默认值
        if (!req.getParameterMap().containsKey(key) || req.getParameterMap().get(key)[0] == null) {
            return defaultValue;
        }
        // 从请求中获取到指定属性的值,若其值仅为“空白字符”,则直接返回给定的默认值
        String value = req.getParameter(key);
        if (StringUtils.isBlank(value)) {
            return defaultValue;
        }
        String encoding = req.getParameter("encoding");
        // 使用指定的字符编码,对属性值进行解码
        return resolveValue(value, encoding);
    }

    private static String resolveValue(String value, String encoding) {
        if (StringUtils.isEmpty(encoding)) {
            encoding = StandardCharsets.UTF_8.name();
        }
        try {
            // 解码
            value = HttpUtils.decode(new String(value.getBytes(StandardCharsets.UTF_8), encoding), encoding);
        } catch (UnsupportedEncodingException ignore) {
        }
        return value.trim();
    }
  1. 若请求map中不包含指定属性的值,或其值为null,则直接返回给定的默认值 public
  2. 请求中获取到指定属性的值,若其值仅为“空白字符”,则直接返回给定的默认值 public
  3. 使用指定的字符编码,对属性值进行解码

WebUtils#required()

    public static String required(final HttpServletRequest req, final String key) {
        // 若请求中不包含指定属性值,则抛出异常
        String value = req.getParameter(key);
        if (StringUtils.isEmpty(value)) {
            throw new IllegalArgumentException("Param '" + key + "' is required.");
        }
        String encoding = req.getParameter("encoding");
        // 解码
        return resolveValue(value, encoding);
    }

若请求中不包含指定属性值,则抛出异常。

checkServiceNameFormat(): 检测serviceName名称是否合法, 必须以@@连接两个字符串

    private void checkServiceNameFormat(String combineServiceName) {
        // 必须以@@连接两个字符串
        String[] split = combineServiceName.split(Constants.SERVICE_INFO_SPLITER);
        if (split.length <= 1) {
            throw new IllegalArgumentException(
                    "Param 'serviceName' is illegal, it should be format as 'groupName@@serviceName");
        }
    }

2.通过请求参数组装出instance

在这里插入图片描述

parseInstance()

    private Instance parseInstance(HttpServletRequest request) throws Exception {

        String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
        String app = WebUtils.optional(request, "app", "DEFAULT");
        // 通过请求中的数据组装出一个instance
        Instance instance = getIpAddress(request);
        // 初始化instance
        instance.setApp(app);
        instance.setServiceName(serviceName);
        // Generate simple instance id first. This value would be updated according to
        // INSTANCE_ID_GENERATOR.
        instance.setInstanceId(instance.generateInstanceId());
        instance.setLastBeat(System.currentTimeMillis());
        String metadata = WebUtils.optional(request, "metadata", StringUtils.EMPTY);
        if (StringUtils.isNotEmpty(metadata)) {
            instance.setMetadata(UtilsAndCommons.parseMetadata(metadata));
        }
        // 验证instance
        instance.validate();

        return instance;
    }
  1. 通过请求中的数据组装出一个instance, getIpAddress(request)
  2. 初始化instance, 加入各种属性, 比如app serviceName instanceId, 最后心跳时间和元数据
  3. 验证instance

在这里插入图片描述

通过请求获取一个instance

    private Instance getIpAddress(HttpServletRequest request) {
        // 从请求中获取各种属性
        final String ip = WebUtils.required(request, "ip");
        final String port = WebUtils.required(request, "port");
        String cluster = WebUtils.optional(request, CommonParams.CLUSTER_NAME, StringUtils.EMPTY);
        if (StringUtils.isBlank(cluster)) {
            cluster = WebUtils.optional(request, "cluster", UtilsAndCommons.DEFAULT_CLUSTER_NAME);
        }
        String enabledString = WebUtils.optional(request, "enabled", StringUtils.EMPTY);
        boolean enabled;
        if (StringUtils.isBlank(enabledString)) {
            enabled = BooleanUtils.toBoolean(WebUtils.optional(request, "enable", "true"));
        } else {
            enabled = BooleanUtils.toBoolean(enabledString);
        }

        boolean ephemeral = BooleanUtils.toBoolean(
                WebUtils.optional(request, "ephemeral", String.valueOf(switchDomain.isDefaultInstanceEphemeral())));

        String weight = WebUtils.optional(request, "weight", "1");
        boolean healthy = BooleanUtils.toBoolean(WebUtils.optional(request, "healthy", "true"));

        // 使用获取到的属性值装配一个instance
        Instance instance = new Instance();
        instance.setPort(Integer.parseInt(port));
        instance.setIp(ip);
        instance.setWeight(Double.parseDouble(weight));
        instance.setClusterName(cluster);
        instance.setHealthy(healthy);
        instance.setEnabled(enabled);
        instance.setEphemeral(ephemeral);

        return instance;
    }

在请求中获取到参数, 然后设置在instance中。

instance.validate(): 验证instance

3.将instance写入到注册表

在这里插入图片描述

ServiceManager#registerInstance()

    public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {

        // 创建一个空service,
        // 注意,第三个参数为true,表示 临时实例
        createEmptyService(namespaceId, serviceName, instance.isEphemeral());
        // 从注册表中获取到service
        Service service = getService(namespaceId, serviceName);

        if (service == null) {
            throw new NacosException(NacosException.INVALID_PARAM,
                    "service not found, namespace: " + namespaceId + ", service: " + serviceName);
        }
        // 将instance写入到service,即写入到了注册表
        addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
    }
  1. 创建一个空的service, 临时的实例
  2. 从注册表中获取service
  3. 将instance写入到service, 写入到注册表中

3.1 创建一个空的service临时的实例

在这里插入图片描述

    public void createServiceIfAbsent(String namespaceId, String serviceName, boolean local, Cluster cluster)
            throws NacosException {
        // 从注册表中获取service
        Service service = getService(namespaceId, serviceName);
        // 若当前注册instance是其提供服务的第一个实例,则注册表中是没有该service的,
        // 此时会创建一个service实例
        if (service == null) {

            Loggers.SRV_LOG.info("creating empty service {}:{}", namespaceId, serviceName);
            service = new Service();
            service.setName(serviceName);
            service.setNamespaceId(namespaceId);
            service.setGroupName(NamingUtils.getGroupName(serviceName));
            // now validate the service. if failed, exception will be thrown
            service.setLastModifiedMillis(System.currentTimeMillis());
            // 重新计算校验和
            service.recalculateChecksum();
            if (cluster != null) {
                // cluster与service建立联系
                // n:1
                cluster.setService(service);
                service.getClusterMap().put(cluster.getName(), cluster);
            }
            service.validate();

            // 将service写入到注册表
            putServiceAndInit(service);
            // 对持久实例的操作
            if (!local) {
                addOrReplaceService(service);
            }
        }
    }

	// 从服务端的注册表中获取Service
    public Service getService(String namespaceId, String serviceName) {
        if (serviceMap.get(namespaceId) == null) {
            return null;
        }
        return chooseServiceMap(namespaceId).get(serviceName);
    }

    public Map<String, Service> chooseServiceMap(String namespaceId) {
        return serviceMap.get(namespaceId);
    }

在这里插入图片描述

  • Service#recalculateChecksum(): 重写计算校验和
  • ServiceManager#putServiceAndInit(): 将service写入到注册表
  • Service#addOrReplaceService(): 持久实例到其他Nacos Server

3.1.1 重写计算校验和

Service#recalculateChecksum(): 重写计算校验和

    public synchronized void recalculateChecksum() {
        // 获取当前service所包含的所有instance列表
        List<Instance> ips = allIPs();

        StringBuilder ipsString = new StringBuilder();
        // 将service所数据追加到ipsString
        ipsString.append(getServiceString());

        if (Loggers.SRV_LOG.isDebugEnabled()) {
            Loggers.SRV_LOG.debug("service to json: " + getServiceString());
        }

        if (CollectionUtils.isNotEmpty(ips)) {
            Collections.sort(ips);
        }

        // 遍历所有instances,将它们的数据进行追加
        for (Instance ip : ips) {
            String string = ip.getIp() + ":" + ip.getPort() + "_" + ip.getWeight() + "_" + ip.isHealthy() + "_" + ip
                    .getClusterName();
            ipsString.append(string);
            ipsString.append(",");
        }

        // 最终获取到当前service的所有数据,经MD5加密后赋值给checksum
        checksum = MD5Utils.md5Hex(ipsString.toString(), Constants.ENCODE);
    }
	// Service.java
    public List<Instance> allIPs() {
        List<Instance> result = new ArrayList<>();
        // 遍历当前service所包含的所有cluster
        for (Map.Entry<String, Cluster> entry : clusterMap.entrySet()) {
            // 将当前遍历cluster中包含的所有instance添加到result
            result.addAll(entry.getValue().allIPs());
        }

        return result;
    }
	// Cluster.java
    public List<Instance> allIPs() {
        List<Instance> allInstances = new ArrayList<>();
        // 添加持久实例
        allInstances.addAll(persistentInstances);
        // 添加临时实例
        allInstances.addAll(ephemeralInstances);
        return allInstances;
    }

在这里插入图片描述

  1. Service.allIPS() -> Cluster.allIPS(): 获取当前service所包含的所有instance列表
    • 遍历当前service所包含的所有cluster
    • 将当前遍历cluster中包含的所有instance添加到result, 返回result
  2. 遍历所有instances,将它们的数据进行追加, 最终获取当前Service的所有数据, 经MD5加密后赋值给checksum

Service的一个属性为checksum, 为校验和, 是当前Service的所有SCI信息的字符串拼接。

在这里插入图片描述

3.1.2 将service写入到注册表

ServiceManager#putServiceAndInit(): 将service写入到注册表

在这里插入图片描述

  1. 将service写入注册表
  2. 初始化service内部健康检测任务
  3. 给nacos集合中的当前服务的持久实例、临时实例添加监听
3.1.2.1 将service写入注册表

ServiceManager#putService(): 将service写入注册表serviceMap

    public void putService(Service service) {
        // 双重检测锁机制  Double Check Lock,DCL
        if (!serviceMap.containsKey(service.getNamespaceId())) {
            synchronized (putServiceLock) {
                if (!serviceMap.containsKey(service.getNamespaceId())) {
                    serviceMap.put(service.getNamespaceId(), new ConcurrentHashMap<>(16));
                }
            }
        }
        // 写入到注册表map
        serviceMap.get(service.getNamespaceId()).put(service.getName(), service);
    }
3.1.2.2 初始化service内部健康检测任务

Service#init(): 初始化service内部健康检测任务

    public void init() {
        // 开启定时清除过期instance任务
        HealthCheckReactor.scheduleCheck(clientBeatCheckTask);
        // 开启了当前service所包含的所有cluster的健康检测任务
        for (Map.Entry<String, Cluster> entry : clusterMap.entrySet()) {
            entry.getValue().setService(this);
            // 开启当前遍历cluster的健康检测任务:
            // 将当前cluster包含的所有instance的心跳检测任务定时添加到一个任务队列
            // taskQueue,即将当前cluster所包含的持久实例的心跳任务添加到taskQueue
            entry.getValue().init();
        }
    }

在这里插入图片描述

开启定时清除过期instance任务, 然后开启了当前service所包含的所有cluster的健康检测任务

3.1.2.2.1 定时清除过期instance任务

Service#init(): 初始化service内部健康检测任务

在这里插入图片描述

ClientBeatCheckTask#run(): 开启定时清除过期instance任务

    @Override
    public void run() {
        try {
            // 若当前service不用当前Server负责,则直接结束
            if (!getDistroMapper().responsible(service.getName())) {
                return;
            }

            // 若当前服务没有开启检测检测功能,则直接结束
            if (!getSwitchDomain().isHealthCheckEnabled()) {
                return;
            }

            // 获取当前服务的所有临时实例
            List<Instance> instances = service.allIPs(true);

            // first set health status of instances:
            // 遍历当前服务的所有临时实例
            for (Instance instance : instances) {
                // 若当前时间距离上次心跳时间已经超过了15s,则将当前instance状态设置为不健康
                if (System.currentTimeMillis() - instance.getLastBeat() > instance.getInstanceHeartBeatTimeOut()) {
                    // 若instance的marked属性不为true,则当前instance可能是临时实例
                    // marked属性若为true,则instance一定为持久实例
                    if (!instance.isMarked()) {
                        // 将healthy状态设置为false
                        if (instance.isHealthy()) {
                            instance.setHealthy(false);
                            Loggers.EVT_LOG
                                    .info("{POS} {IP-DISABLED} valid: {}:{}@{}@{}, region: {}, msg: client timeout after {}, last beat: {}",
                                            instance.getIp(), instance.getPort(), instance.getClusterName(),
                                            service.getName(), UtilsAndCommons.LOCALHOST_SITE,
                                            instance.getInstanceHeartBeatTimeOut(), instance.getLastBeat());
                            // 发布状态变更事件
                            getPushService().serviceChanged(service);
                            ApplicationUtils.publishEvent(new InstanceHeartbeatTimeoutEvent(this, instance));
                        }
                    }
                }
            }

            if (!getGlobalConfig().isExpireInstance()) {
                return;
            }

            // then remove obsolete instances:
            // 遍历所有临时实例
            for (Instance instance : instances) {

                // 若当前instance被标记了,说明其为过期的持久实例,直接跳过
                if (instance.isMarked()) {
                    continue;
                }

                // 若当前时间与上次心跳时间间隔超过了30s,则将当前instance清除
                if (System.currentTimeMillis() - instance.getLastBeat() > instance.getIpDeleteTimeout()) {
                    // delete instance
                    Loggers.SRV_LOG.info("[AUTO-DELETE-IP] service: {}, ip: {}", service.getName(),
                            JacksonUtils.toJson(instance));
                    // 清除
                    deleteIp(instance);
                }
            }

        } catch (Exception e) {
            Loggers.SRV_LOG.warn("Exception while processing client beat time out.", e);
        }

    }
  1. 若当前service不用当前Server负责,则直接结束
  2. 当前服务没有开启检测检测功能,则直接结束
  3. 获取当前服务的所有临时实例, 遍历当前服务的所有临时实例, 判断是否心跳时间超过15s, 是否是临时实例, 将healthy状态设置为false
  4. 遍历所有临时实例, 如果是过期的持久实例, 则直接跳过, 若当前时间与上次心跳时间间隔超过了30s,则将当前instance清除deleteIp()

deleteIp(): 清除当前instance。

   private void deleteIp(Instance instance) {

        try {
            // 构建并初始化一个request
            NamingProxy.Request request = NamingProxy.Request.newRequest();
            request.appendParam("ip", instance.getIp()).appendParam("port", String.valueOf(instance.getPort()))
                    .appendParam("ephemeral", "true").appendParam("clusterName", instance.getClusterName())
                    .appendParam("serviceName", service.getName()).appendParam("namespaceId", service.getNamespaceId());

            // 构建一个访问自己的请求url
            String url = "http://127.0.0.1:" + ApplicationUtils.getPort() + ApplicationUtils.getContextPath()
                    + UtilsAndCommons.NACOS_NAMING_CONTEXT + "/instance?" + request.toUrl();

            // delete instance asynchronously:
            // 调用 Nacos 自研的HttpClient完成Server间的请求提交,
            // 该HttpClient是对Apache的Http异步Client的封装
            HttpClient.asyncHttpDelete(url, null, null, new Callback<String>() {
                @Override
                public void onReceive(RestResult<String> result) {
                    if (!result.ok()) {
                        Loggers.SRV_LOG
                                .error("[IP-DEAD] failed to delete ip automatically, ip: {}, caused {}, resp code: {}",
                                        instance.toJson(), result.getMessage(), result.getCode());
                    }
                }

                @Override
                public void onError(Throwable throwable) {
                    Loggers.SRV_LOG
                            .error("[IP-DEAD] failed to delete ip automatically, ip: {}, error: {}", instance.toJson(),
                                    throwable);
                }

                @Override
                public void onCancel() {

                }
            });

        } catch (Exception e) {
            Loggers.SRV_LOG
                    .error("[IP-DEAD] failed to delete ip automatically, ip: {}, error: {}", instance.toJson(), e);
        }
    }

通过构建的request和url, 调用 Nacos 自研的HttpClient完成Server间的请求提交

3.1.2.2.2 cluster的健康检测任务

Cluster#init(): 初始化Cluster内部健康检测任务

在这里插入图片描述

在这里插入图片描述

先创建健康检查任务, 再开启。

HealthCheckTask#run(): 健康检查。

    @Override
    public void run() {

        try {
            if (distroMapper.responsible(cluster.getService().getName()) && switchDomain
                    .isHealthCheckEnabled(cluster.getService().getName())) {
                // 处理该任务
                healthCheckProcessor.process(this);
                if (Loggers.EVT_LOG.isDebugEnabled()) {
                    Loggers.EVT_LOG
                            .debug("[HEALTH-CHECK] schedule health check task: {}", cluster.getService().getName());
                }
            }
        } catch (Throwable e) {
            Loggers.SRV_LOG
                    .error("[HEALTH-CHECK] error while process health check for {}:{}", cluster.getService().getName(),
                            cluster.getName(), e);
        } finally {
            if (!cancelled) {
                // 开启下一次任务
                HealthCheckReactor.scheduleCheck(this);

                // worst == 0 means never checked
                if (this.getCheckRtWorst() > 0 && switchDomain.isHealthCheckEnabled(cluster.getService().getName())
                        && distroMapper.responsible(cluster.getService().getName())) {
                    // TLog doesn't support float so we must convert it into long
                    long diff =
                            ((this.getCheckRtLast() - this.getCheckRtLastLast()) * 10000) / this.getCheckRtLastLast();

                    this.setCheckRtLastLast(this.getCheckRtLast());

                    Cluster cluster = this.getCluster();

                    if (Loggers.CHECK_RT.isDebugEnabled()) {
                        Loggers.CHECK_RT.debug("{}:{}@{}->normalized: {}, worst: {}, best: {}, last: {}, diff: {}",
                                cluster.getService().getName(), cluster.getName(), cluster.getHealthChecker().getType(),
                                this.getCheckRtNormalized(), this.getCheckRtWorst(), this.getCheckRtBest(),
                                this.getCheckRtLast(), diff);
                    }
                }
            }
        }
    }
  1. try-cache中处理该任务
  2. finally中开启下一次任务

在这里插入图片描述

在这里插入图片描述

    public void process(HealthCheckTask task) {
        // 获取当前cluster中包含的持久实例
        List<Instance> ips = task.getCluster().allIPs(false);

        if (CollectionUtils.isEmpty(ips)) {
            return;
        }

        // 遍历所有持久实例
        for (Instance ip : ips) {
            // 若当前遍历instance过期,则跳过
            if (ip.isMarked()) {
                if (SRV_LOG.isDebugEnabled()) {
                    SRV_LOG.debug("tcp check, ip is marked as to skip health check, ip:" + ip.getIp());
                }
                continue;
            }

            if (!ip.markChecking()) {
                SRV_LOG.warn("tcp check started before last one finished, service: " + task.getCluster().getService()
                        .getName() + ":" + task.getCluster().getName() + ":" + ip.getIp() + ":" + ip.getPort());

                healthCheckCommon
                        .reEvaluateCheckRT(task.getCheckRtNormalized() * 2, task, switchDomain.getTcpHealthParams());
                continue;
            }
            // 生成一个心跳实例
            Beat beat = new Beat(ip, task);
            // 将心跳实例写入到queue
            taskQueue.add(beat);
            MetricsMonitor.getTcpHealthCheckMonitor().incrementAndGet();
        }
    }
  1. 获取当前cluster中包含的持久实例
  2. 遍历所有持久实例, 若instance过期, 则跳过
  3. 生成一个心跳实例, 心跳实例放入任务队列taskQueue, 即将当前cluster所包含的持久实例的心跳任务添加到taskQueue

3.1.3 持久实例到其他Nacos Server

Service#addOrReplaceService(): 持久实例到其他Nacos Server

    // 一致性服务
    @Resource(name = "consistencyDelegate")
    private ConsistencyService consistencyService;
 
    public void addOrReplaceService(Service service) throws NacosException {
        // 将这个service同步到其它nacos server
        consistencyService.put(KeyBuilder.buildServiceMetaKey(service.getNamespaceId(), service.getName()), service);
    }

3.2 将instance写入到service

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

ServiceManager#updateIpAddresses(): 修改当前service的instance列表, 添加或者删除实例

    public List<Instance> updateIpAddresses(Service service, String action, boolean ephemeral, Instance... ips)
            throws NacosException {

        // 从其它nacos获取当前服务数据(临时实例数据)
        Datum datum = consistencyService
                .get(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), ephemeral));

        // 获取本地注册表中当前服务的所有临时实例
        List<Instance> currentIPs = service.allIPs(ephemeral);
        Map<String, Instance> currentInstances = new HashMap<>(currentIPs.size());
        Set<String> currentInstanceIds = Sets.newHashSet();

        // 遍历注册表中获取到的实例
        for (Instance instance : currentIPs) {
            // 将当前遍历的instance写入到map,key为ip:port,value为instance
            currentInstances.put(instance.toIpAddr(), instance);
            // 将当前遍历的instanceId写入到一个set
            currentInstanceIds.add(instance.getInstanceId());
        }

        Map<String, Instance> instanceMap;
        if (datum != null) {
            // 将注册表中主机的instance数据替换掉外来的相同主机的instance数据
            instanceMap = setValid(((Instances) datum.value).getInstanceList(), currentInstances);
        } else {
            instanceMap = new HashMap<>(ips.length);
        }

        for (Instance instance : ips) {
            // 若当前service中不包含当前要注册的instance所属cluster,则创建一个
            if (!service.getClusterMap().containsKey(instance.getClusterName())) {
                Cluster cluster = new Cluster(instance.getClusterName(), service);
                // 初始化cluster的健康检测任务
                cluster.init();
                service.getClusterMap().put(instance.getClusterName(), cluster);
                Loggers.SRV_LOG
                        .warn("cluster: {} not found, ip: {}, will create new cluster with default configuration.",
                                instance.getClusterName(), instance.toJson());
            }

            // 若当前操作为清除操作,则将当前instance从instanceMap中清除,
            // 否则就是添加操作,即将当前instance添加到instanceMap中
            if (UtilsAndCommons.UPDATE_INSTANCE_ACTION_REMOVE.equals(action)) {
                instanceMap.remove(instance.getDatumKey());
            } else {
                instance.setInstanceId(instance.generateInstanceId(currentInstanceIds));
                instanceMap.put(instance.getDatumKey(), instance);
            }

        }

        if (instanceMap.size() <= 0 && UtilsAndCommons.UPDATE_INSTANCE_ACTION_ADD.equals(action)) {
            throw new IllegalArgumentException(
                    "ip list can not be empty, service: " + service.getName() + ", ip list: " + JacksonUtils
                            .toJson(instanceMap.values()));
        }

        return new ArrayList<>(instanceMap.values());
    }
  1. 从其它nacos获取当前服务数据(临时实例数据)
  2. 获取本地注册表中当前服务的所有临时实例
  3. 遍历注册表中获取到的实例, 将当前遍历的instance写入到map, 将当前遍历的instanceId写入到一个set
  4. 将注册表中主机的instance数据替换掉外来的相同主机的instance数据
  5. 若当前service中不包含当前要注册的instance所属cluster,则创建一个, 初始化cluster的健康检查任务
  6. 返回修改后的instance列表

将注册表中主机的instance数据替换掉外来的相同主机的instance数据 setValid():

在这里插入图片描述

4.总结

  1. 创建空的Service
  2. 将instance写入到service,即写入到了注册表

在这里插入图片描述

创建空的Service: 需要将service写入到注册表, 并且初始化service内部健康检测任务, 在初始化service内部健康的时候会开启定时清除过期instance任务和开启当前service的cluster的健康检查任务。

在这里插入图片描述

将instance写入到service,即写入到了注册表

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/563845.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软考数据库详细知识点整理(全)

目录 第一章 计算机系统基本知识 1.1 计算机系统 1.1.1 计算机硬件组成 1.1.2 中央处理单元 1.1.3 数据表示 1.1.4 校验码 1.2 计算机体系结构 1.2.1 体系结构分类 1.2.2 指令系统存 1.2.3 储系系统 1.2.4 输入/输出技术 1.2.5 总线结构 1.3 可靠性、性能、安全 …

MySQL数据库---笔记3

MySQL数据库---笔记3 一、储存引擎1.1、MySQL体系结构1.2、存储引擎简介1.3、存储引擎特点1.3、存储引擎选择 二、索引 一、储存引擎 1.1、MySQL体系结构 MySQL体系结构图 连接层 最上层是一些客户端和链接服务&#xff0c;主要完成一些类似于连接处理、授权认证、及相关的安…

MySQL索引优化实战EXPLAIN解析

先来介绍一下具体的业务场景 当用户登录后&#xff0c;需要查看能够学习的课程&#xff0c;不同的用户看到的课程是不同的&#xff0c;课程存在权限&#xff0c;权限是被下面lesson_user_permissions表控制的&#xff0c;其中sys_user_id 和 lesson_id 作为联合主键 另外还有一…

Prompt Engineering | 对话聊天prompt

&#x1f604; 使用LLM来搭建一个定制的聊天机器人&#xff0c;只需要很少的工作量。 ⭐ 本文将讲解如何利用聊天格式与个性化或专门针对特兹那个任务或行为的聊天机器人进行多伦对话。 文章目录 1、提供对话的早期部分&#xff0c;引导模型继续聊天2、示例&#xff1a;构建一个…

基于ZeroTier虚拟网络搭建分流策略及创建IPv6网络

假设无数个成员&#xff08;移动设备、终端、Docker&#xff09;&#xff0c;需要劫持所有请求&#xff0c;并根据它们请求所访问的域名解析出IPv6或者IPv4地址来进行有效的选择性访问 说的直白一点点就是&#xff1a;分流策略 这里例一下需要做的工作&#xff0c;来看一下&am…

多维时序预测 | Matlab基于最小二乘支持向量机LSSVM多维时间序列预测,LSSVM多变量时间序列预测

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 基于最小二乘支持向量机LSSVM多维时间序列预测LSSVM多变量时间序列预测,matlab代码 评价指标包括:MAPE、MAE、RMSE和R2等,代码质量极高,

【毕业季】青春散场,我们期待下一场的开幕

青春&#xff0c;是一段追梦的旅程&#xff0c;毕业&#xff0c;是一次释放梦想的契机&#xff0c;祝愿每位毕业生在新的征程中&#xff0c;勇往直前&#xff0c;追逐梦想。 目录 青春散场&#xff0c;我们期待下一场的开幕 回忆过去 憧憬未来 青春散场&#xff0c;我们期待…

做好功能测试需要的8项基本技能【点工进来】

功能测试是测试工程师的基础功&#xff0c;很多人功能测试还做不好&#xff0c;就想去做性能测试、自动化测试。很多人对功能测试的理解就是点点点&#xff0c;如何自己不用心去悟&#xff0c;去研究&#xff0c;那么你的职业生涯也就停留在点点点上了。在这里&#xff0c;我把…

重磅新书上市,带你看看了不起的芯片!

千呼万唤始出来&#xff0c;我的第一本书《了不起的芯片》今天正式和大家见面啦! 任何一本书的背后都有一段不为人知的曲折故事&#xff0c;在此和大家分享一下我写这本书的心路历程。希望我的经历对你能有一些帮助&#xff0c;也希望你能喜欢我的作品。我还为大家申请了专属优…

vite3+vue3 项目打包优化三 — CDN加速、文件压缩

1. CDN在线加速 内容分发网络&#xff08;Content Delivery Network&#xff0c;简称 CDN&#xff09;&#xff0c;是构建在数据网络上的一种分布式内容分发网&#xff0c;它可以让用户从最近的服务器请求资源&#xff0c;以提升网络请求的响应速度。 通常情况下&#xff0c;…

tcp套接字的应用

tcp服务端流程 tcp客户端流程 客户端代码 tcpClient.hpp #include<iostream> #include<string> #include<cstring> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in…

DG4pros高楼区地籍建模项目报告

引言 据睿铂统计&#xff0c;目前约70%倾斜摄影相机都用于测量项目&#xff0c;其中绝大部分是地籍测量相关项目。例如黑龙江某客户已使用睿铂相机累计完成约1000平方公里的地籍项目。倾斜摄影技术虽然在农村地籍测量项目中应用较好&#xff0c;但却无法解决高楼区域地籍测量的…

使用 SD-WAN 实现企业级 WAN 敏捷性、简易性和性能

VMware SD-WAN 提高了敏捷性和成本效益&#xff0c;同时确保跨 WAN 的应用性能。 当今的分支机构用户正在使用更多的广域网 (WAN) 带宽&#xff0c;因为他们需要通过 Zoom、WebEx、Microsoft 365 等工具进行在线协作&#xff0c;更多地用到 “软件即服务”(SaaS) 和云计算服务…

【python资料】pandas的条件查询

一、说明 在使用Pandas的DataFrame进行数据挖掘的时候&#xff0c;需要形形色色的条件查询&#xff0c;但是这些查询的基本语法是啥&#xff0c;查询的灵活性如何&#xff0c;本文将对他们进行详细列出&#xff0c;便于以后查阅。 二、Pandas条件查询方法 2.1 简单条件查询 1、…

「API接口的技术说明文档

API技术文档是指软件系统或应用程序的API接口的技术说明文档。它详细描述了API端点&#xff0c;资源&#xff0c;参数&#xff0c;请求和响应格式以及使用API的常见用例和最佳实践。 1.为什么需要API技术文档&#xff1f; API技术文档是API的唯一方法&#xff0c;以便开发人员…

分享18个好用的ChatGPT插件

上周ChatGPT又进化了&#xff0c;支持联网还有70几种第三方插件&#xff0c;不过还是老样子&#xff0c;只服务氪金玩家&#xff0c;免费端可能还得等等。之前只开放了俩插件&#xff0c;网络浏览器和代码解释器&#xff0c;只能说是真的不够用。 ChatGPT&#xff1a;不够&…

中间件(三)- Kafka(二)

Kafka 6. 高效读写&Zookeeper作用6.1 Kafka的高效读写6.2 Kafka中zookeeper的作用 7. 事务7.1 Producer事务7.2 Consumer事务 8. API生产者流程9. 通过python调用kafka9.1 安装插件9.2 生产者&#xff08;Producer&#xff09;与消费者&#xff08;Consumer&#xff09;9.3…

【实验】SegViT: Semantic Segmentation with Plain Vision Transformers

想要借鉴SegViT官方模型源码部署到本地自己代码文件中 1. 环境配置 官网要求安装mmcv-full1.4.4和mmsegmentation0.24.0 在这之前记得把mmcv和mmsegmentation原来版本卸载 pip uninstall mmcv pip uninstall mmcv-full pip uninstall mmsegmentation安装mmcv 其中&#xff…

你若在患难之日胆怯,你的力量便微小

如果你在做一件事情之前就过分放大它的困难&#xff0c;这就会逐渐降低自己去做它的动机和动力&#xff0c;还没开始你就已经削弱了自己的行动能力&#xff0c;在气势上就已经输了。 不要害怕困难&#xff0c;勇敢的去面对问题&#xff0c;解决问题&#xff0c;你就会在气势上更…

RK平台烧录固件的几种模式

在RK平台开发过程中&#xff0c;我们在使用烧录工具烧写固件的时候经常可以看到烧录工具会显示当前PC识别到的设备类型&#xff0c;一般有&#xff1a;MASKROM&#xff0c;LOADER&#xff0c;ADB&#xff0c;MSC等等。能烧录固件的模式有MASKROM模式和LOADER模式&#xff0c;下…