微服务框架 SpringCloud微服务架构 微服务面试篇 54 微服务篇 54.5 Nacos与Eureka的区别有哪些?【接口方式、实例类型、健康检测】

news2025/2/2 12:43:57

微服务框架

【SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式,系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】

微服务面试篇

文章目录

      • 微服务框架
      • 微服务面试篇
      • 54 微服务篇
        • 54.5 Nacos与Eureka的区别有哪些?【接口方式、实例类型、健康检测】
          • 54.5.1 Nacos 客户端源码

54 微服务篇

54.5 Nacos与Eureka的区别有哪些?【接口方式、实例类型、健康检测】

54.5.1 Nacos 客户端源码

先来直接看 看 客户端是 如何完成服务注册的

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

在这里插入图片描述

首先这个依赖要引入 进来

看看这个依赖

在这里插入图片描述

首先它是 一个starter,实现了 自动装配,

以前学过 自动装配 会去读取

在这里插入图片描述

这个文件,看看

在这里插入图片描述

里面我们 现在要研究的就是 NacosServiceRegistryAutoConfiguration 服务 注册 的自动装配

点进去看看

在这里插入图片描述

可以看到, 它是一个 配置类嘛

里面注册了 很多很多的bean

其中

@Bean
public NacosServiceRegistry nacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
    return new NacosServiceRegistry(nacosDiscoveryProperties);
}

在这里插入图片描述

这个类 就是用来完成 服务的注册 的

然后下面 还有一个

在这里插入图片描述

自动服务注册,平时我们一 启动服务就完成注册 ,就是依靠 这个类 来完成的

OK,进到 NacosServiceRegistry 这个类

在这里插入图片描述

进到这个 registerInstance 这个方法

@Override
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
    
    // 检查instance 是否合法
    NamingUtils.checkInstanceIsLegal(instance);
    String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
    
    // 临时实例才做 心跳,永久实例 不做心跳
    if (instance.isEphemeral()) {
        
        // 心跳
        BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance);
        beatReactor.addBeatInfo(groupedServiceName, beatInfo);
    }
    
    // 服务注册
    serverProxy.registerService(groupedServiceName, groupName, instance);
}

在这里插入图片描述

看看 心跳的发送

官方文档:https://nacos.io/zh-cn/docs/open-api.html

在这里插入图片描述

对应代码中

public JsonNode sendBeat(BeatInfo beatInfo, boolean lightBeatEnabled) throws NacosException {
    
    if (NAMING_LOGGER.isDebugEnabled()) {
        NAMING_LOGGER.debug("[BEAT] {} sending beat to server: {}", namespaceId, beatInfo.toString());
    }
    Map<String, String> params = new HashMap<String, String>(8);
    Map<String, String> bodyMap = new HashMap<String, String>(2);
    if (!lightBeatEnabled) {
        bodyMap.put("beat", JacksonUtils.toJson(beatInfo));
    }
    params.put(CommonParams.NAMESPACE_ID, namespaceId);
    params.put(CommonParams.SERVICE_NAME, beatInfo.getServiceName());
    params.put(CommonParams.CLUSTER_NAME, beatInfo.getCluster());
    params.put("ip", beatInfo.getIp());
    params.put("port", String.valueOf(beatInfo.getPort()));
    String result = reqApi(UtilAndComs.nacosUrlBase + "/instance/beat", params, bodyMap, HttpMethod.PUT);
    return JacksonUtils.toObj(result);
}

在这里插入图片描述

就是这样 心跳就发出去 了

客户端 心跳发送完成后 ,也是由 InstanceController 进行接收

@CanDistro
@PutMapping("/beat")
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
public ObjectNode beat(HttpServletRequest request) throws Exception {
    
    ObjectNode result = JacksonUtils.createEmptyJsonNode();
    result.put(SwitchEntry.CLIENT_BEAT_INTERVAL, switchDomain.getClientBeatInterval());
    
    String beat = WebUtils.optional(request, "beat", StringUtils.EMPTY);
    
    // 从request 中解析心跳信息
    RsInfo clientBeat = null;
    if (StringUtils.isNotBlank(beat)) {
        clientBeat = JacksonUtils.toObj(beat, RsInfo.class);
    }
    String clusterName = WebUtils
            .optional(request, CommonParams.CLUSTER_NAME, UtilsAndCommons.DEFAULT_CLUSTER_NAME);
    String ip = WebUtils.optional(request, "ip", StringUtils.EMPTY);
    int port = Integer.parseInt(WebUtils.optional(request, "port", "0"));
    if (clientBeat != null) {
        if (StringUtils.isNotBlank(clientBeat.getCluster())) {
            clusterName = clientBeat.getCluster();
        } else {
            // fix #2533
            clientBeat.setCluster(clusterName);
        }
        ip = clientBeat.getIp();
        port = clientBeat.getPort();
    }
    String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
    String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
    NamingUtils.checkServiceNameFormat(serviceName);
    Loggers.SRV_LOG.debug("[CLIENT-BEAT] full arguments: beat: {}, serviceName: {}", clientBeat, serviceName);
    
    // 通过namespaceId、serviceName、clusterName、ip、port 从注册表中获取Instance 对象
    Instance instance = serviceManager.getInstance(namespaceId, serviceName, clusterName, ip, port);
    
    if (instance == null) {
        
        // 心跳的实例不存在,需要重新注册
        if (clientBeat == null) {
            result.put(CommonParams.CODE, NamingResponseCode.RESOURCE_NOT_FOUND);
            return result;
        }
        
        Loggers.SRV_LOG.warn("[CLIENT-BEAT] The instance has been removed for health mechanism, "
                + "perform data compensation operations, beat: {}, serviceName: {}", clientBeat, serviceName);
        
        instance = new Instance();
        instance.setPort(clientBeat.getPort());
        instance.setIp(clientBeat.getIp());
        instance.setWeight(clientBeat.getWeight());
        instance.setMetadata(clientBeat.getMetadata());
        instance.setClusterName(clusterName);
        instance.setServiceName(serviceName);
        instance.setInstanceId(instance.getInstanceId());
        instance.setEphemeral(clientBeat.isEphemeral());
        
        serviceManager.registerInstance(namespaceId, serviceName, instance);
    }
    
    // 获取对应的注册表 中的Service 信息
    Service service = serviceManager.getService(namespaceId, serviceName);
    
    if (service == null) {
        throw new NacosException(NacosException.SERVER_ERROR,
                "service not found: " + serviceName + "@" + namespaceId);
    }
    if (clientBeat == null) {
        clientBeat = new RsInfo();
        clientBeat.setIp(ip);
        clientBeat.setPort(port);
        clientBeat.setCluster(clusterName);
    }
    
    //处理服务中 的实例心跳
    service.processClientBeat(clientBeat);
    
    result.put(CommonParams.CODE, NamingResponseCode.OK);
    if (instance.containsMetadata(PreservedMetadataKeys.HEART_BEAT_INTERVAL)) {
        result.put(SwitchEntry.CLIENT_BEAT_INTERVAL, instance.getInstanceHeartBeatInterval());
    }
    result.put(SwitchEntry.LIGHT_BEAT_ENABLED, switchDomain.isLightBeatEnabled());
    return result;
}

在这里插入图片描述

//处理服务中 的实例心跳
service.processClientBeat(clientBeat);

注意这个

我们 跟进 这个方法

public void processClientBeat(final RsInfo rsInfo) {
    ClientBeatProcessor clientBeatProcessor = new ClientBeatProcessor();
    clientBeatProcessor.setService(this);
    clientBeatProcessor.setRsInfo(rsInfo);
    HealthCheckReactor.scheduleNow(clientBeatProcessor);
}

OK,再跟进 scheduleNow 这个方法

public static ScheduledFuture<?> scheduleNow(Runnable task) {
    return GlobalExecutor.scheduleNamingHealth(task, 0, TimeUnit.MILLISECONDS);
}

可以看到 又是线程池,又是异步执行

跟进ClientBeatProcessor 这个类

在这里插入图片描述

里面有个 run 方法

@Override
public void run() {
    
    // 得到服务
    Service service = this.service;
    if (Loggers.EVT_LOG.isDebugEnabled()) {
        Loggers.EVT_LOG.debug("[CLIENT-BEAT] processing beat: {}", rsInfo.toString());
    }
    
    String ip = rsInfo.getIp();
    String clusterName = rsInfo.getCluster();
    int port = rsInfo.getPort();
    
    // 获取注册表中 的对应集群
    Cluster cluster = service.getClusterMap().get(clusterName);
    
    // 得到 集群中的实例列表
    List<Instance> instances = cluster.allIPs(true);
    
    for (Instance instance : instances) {
        
        //判断实例的ip、port 是否与心跳实例的ip、port 一致,如果一致,则证明是当前实例的心跳
        if (instance.getIp().equals(ip) && instance.getPort() == port) {
            if (Loggers.EVT_LOG.isDebugEnabled()) {
                Loggers.EVT_LOG.debug("[CLIENT-BEAT] refresh beat: {}", rsInfo.toString());
            }
            
            // 更新最后一次 心跳的时间
            instance.setLastBeat(System.currentTimeMillis());
            if (!instance.isMarked()) {
                if (!instance.isHealthy()) {
                    instance.setHealthy(true);
                    Loggers.EVT_LOG
                            .info("service: {} {POS} {IP-ENABLED} valid: {}:{}@{}, region: {}, msg: client beat ok",
                                    cluster.getService().getName(), ip, port, cluster.getName(),
                                    UtilsAndCommons.LOCALHOST_SITE);
                    
                    // 发布服务状态 变更的事件
                    getPushService().serviceChanged(service);
                }
            }
        }
    }
}

这就完成了 心跳实例的修改,当然这都是 服务端的处理,

现在但是,如果现在有一个服务 的心跳不正常,那要何时去把这个实例 给干掉呢?

回到我们当时的服务注册 逻辑

在这里插入图片描述

跟进这个方法

在这里插入图片描述

进入第一个 创建空 服务的方法

在这里插入图片描述

注意这个初始化

跟进这个方法

在这里插入图片描述

跟进这个 init 【这个方法 就是在初始化 对某个 服务的健康检测】

public void init() {
    
    // 初始化服务,开启心跳的健康检测
    HealthCheckReactor.scheduleCheck(clientBeatCheckTask); // 就是这行
    for (Map.Entry<String, Cluster> entry : clusterMap.entrySet()) {
        entry.getValue().setService(this);
        // 集群 初始化
        entry.getValue().init();
    }
}

在这里插入图片描述

scheduleCheck 跟进这个方法

在这里插入图片描述

没啥问题,心跳检查,周期也在

跟进 ClientBeatCheckTask 这个 任务的run 方法

@Override
public void run() {
    try {
        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) {
            
            // 判断 实例的最后一次心跳时间 距离现在的 时间间隔,如果大于 15,超时
            if (System.currentTimeMillis() - instance.getLastBeat() > instance.getInstanceHeartBeatTimeOut()) {
                if (!instance.isMarked()) {
                    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) {
            
            if (instance.isMarked()) {
                continue;
            }
            
            // // 判断 实例的最后一次心跳时间 距离现在的 时间间隔,大于30 s,则代表需要被删除
            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);
    }
    
}

在这里插入图片描述

接着 进到 HealthCheckTask 的run 方法

在这里插入图片描述

跟进 process 方法

@Override
public void process(HealthCheckTask task) {
    
    //得到 集群中的所有实例的IP 地址
    List<Instance> ips = task.getCluster().allIPs(false);
    
    if (CollectionUtils.isEmpty(ips)) {
        return;
    }
    
    for (Instance ip : ips) {
        
        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);
        taskQueue.add(beat);
        MetricsMonitor.getTcpHealthCheckMonitor().incrementAndGet();
    }
}

在这里插入图片描述

OK,可以回到 我们的问题了

Nacos与Eureka的区别有哪些?

问题说明:考察对Nacos、Eureka的底层实现的掌握情况

难易程度:难

参考话术

Nacos与Eureka有相同点,也有不同之处,可以从以下几点来描述:

  • 接口方式:Nacos与Eureka都对外暴露了Rest风格的API接口,用来实现服务注册、发现等功能
  • 实例类型:Nacos的实例有永久和临时实例之分;而Eureka只支持临时实例
  • 健康检测:Nacos对临时实例采用心跳模式检测,对永久实例采用主动请求来检测;Eureka只支持心跳模式
  • 服务发现:Nacos支持定时拉取和订阅推送两种模式;Eureka只支持定时拉取模式

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

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

相关文章

【大数据入门核心技术-Kafka】(七)Kafka扩容broker和数据迁移

目录 一、准备工作 1、安装好Zookeeper集群 2、安装好Kafka集群 二、Kafka扩容broker 三、Kafka数据迁移 1、查看主题列表 2、创建Topic 3、查看Topic详细信息 4、生成需要迁移的json 5、生成迁移计划 6、执行迁移计划 7、查看迁移计划 8、确认topic数据分布 一、…

Shiro框架学习笔记、整合Springboot、redis缓存

本笔记基于B站UP主不良人编程 目录 1.权限的管理 1.1什么是权限管理 1.2什么是身份认证 1.3什么是授权 2.什么是Shiro 3.Shiro的核心架构 3.1 S核心内容 4.shiro中的认证4.1认证 4.2shiro中认证的关键对象 4.3认证流程 4.4认证程序开发流程 4.4认证程序源码 4.5自定…

java ssm羽毛球馆管理和交流平台系统

羽毛球作为每个人爱好的一项体育运动&#xff0c;越来也收到人们的好评和关注。很多羽毛球爱好者通过网站的形式对羽毛球场地情况&#xff0c;羽毛球的爱好者的互相学习进行交流&#xff0c;方便了大众对于羽毛球的交流和沟通&#xff0c;提高羽毛球技术的同时&#xff0c;也让…

颠覆传统返利模式,针对用户复购率低的全新解决方案——消费盲返

如今互联网商业模式遍地开花&#xff0c;谈及商业模式&#xff0c;大家第一想到的肯定是积分消费返利&#xff0c;那么“消费返利”对于大家来说都不陌生&#xff0c;那么本期林工想给大家介绍的是一个怎么听怎么亏&#xff0c;实则已经悄悄爆火的模式——消费盲返&#xff0c;…

DNS寻址过程

文章目录什么是DNS寻址过程图显示什么是DNS DNS是域名系统( Domain Name System)的英文缩写&#xff0c;是一种组织成域层次结构的计算机和网络服务命名系统&#xff0c;它用于TCP/IP网络&#xff0c;它所提供的服务是用来将主机名和域名转换为IP地址的工作。 寻址过程 本地…

【Linux】第三部分 Linux文件系统目录结构

【Linux】第三部分 Linux文件系统目录结构 文章目录【Linux】第三部分 Linux文件系统目录结构3. Linux文件系统目录结构总结3. Linux文件系统目录结构 可以右键打开终端 目录意义bin该目录存放的是经常使用到的命令&#xff0c;例如上图所示&#xff1a;cd &#xff0c; ls 等…

《计算机程序构造与解释》读书笔记(3)

文章目录1. 写在最前面2. 设计的取舍3. 赋值和局部状态3.1 局部状态变量3.2 引进赋值带来的利益3.3 引进赋值的代价3.3.1 同一和变化3.3.2 命令式程序设计的缺陷4. 求值的环境模型4.1 求值规则4.2 简单过程的应用4.3 将框架看做局部状态的展台4.4 内部定义5. 用变动数据做模拟5…

Mycat(13):全局表和普通表的配置和测试

1 全局表概述 一个真实的业务系统中&#xff0c;往往存在大量的类似字典表的表格&#xff0c;它们与业务表之间可能有关系&#xff0c;这种关系&#xff0c;可以理解为“标签”&#xff0c;而不应理解为通常的“主从关系”&#xff0c;这些表基本上很少变动&#xff0c;可以根…

Spring Security认证和授权

Spring Security认证和授权 一、Spring Security的认识 Spring Security是Spring家族中的一个安全管理框架。相比与另外一个安全框架Shiro&#xff0c;它提供了更丰富的功能&#xff0c;社区资源也比Shiro丰富。 一般来说中大型的项目都是使用SpringSecurity来做安全框架。小…

Windows C语言 UDP通信demo

目录编译环境快速入门编译指令服务端code客户端code参考文章以及遇到的问题编译环境 我的demo是通过此文章从C更改成的C&#xff0c;编译环境使用的是Mingw&#xff0c;如下图所示 快速入门 拷贝代码编译互传消息 编译指令 客户端&#xff1a;gcc .\udpclient.c -lwsock3…

《剑指offer》– 链表中倒数第k个节点、反转链表、合并两个排序的链表

一、链表中倒数时第k个节点&#xff1a; 1、题目&#xff1a; 输入一个链表&#xff0c;输出该链表中倒数第k个结点。 2、解题思路&#xff1a;单链表具有单向移动的特性。 &#xff08;1&#xff09;第一种&#xff1a;先遍历链表&#xff0c;算出链表节点数count&#xf…

计算机毕设Python+Vue学生用品采购系统(程序+LW+部署)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

我国风电行业发展现状:并网装机容量持续增长 产业集中化趋势明显

根据观研报告网发布的《2022年中国风电行业分析报告-行业全景评估与投资规划分析》显示&#xff0c;风电是一种清洁、绿色的可再生能源。风力发电是能源领域中技术最成熟、最具规模开发条件和商业化发展前景的发电方式之一。发展风力发电对于解决能源危机、减轻环境污染、调整能…

【Java面试八股文宝典之基础篇】备战2023 查缺补漏 你越早准备 越早成功!!!——Day13

大家好&#xff0c;我是陶然同学&#xff0c;软件工程大三明年实习。认识我的朋友们知道&#xff0c;我是科班出身&#xff0c;学的还行&#xff0c;但是对面试掌握不够&#xff0c;所以我将用这100多天更新Java面试题&#x1f643;&#x1f643;。 不敢苟同&#xff0c;相信大…

【1760. 袋子里最少数目的球】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给你一个整数数组 nums &#xff0c;其中 nums[i] 表示第 i 个袋子里球的数目。同时给你一个整数 maxOperations 。 你可以进行如下操作至多 maxOperations 次&#xff1a; 选择任意一个袋子&#…

开发板到货记录一波

今天在终于拿到了期待已久的开发板RK3568&#xff0c;来&#xff0c;亮个相吧&#xff01;&#xff01;&#xff01; 开发板资源还是相当丰富的&#xff0c;对于学习安卓&Linux都是非常友好的&#xff0c;开发板默认安装的是安卓11系统&#xff0c;由于经费问题目前还没有…

小米发明“永动机”:走路即可为智能设备充电

蓝牙耳机、智能智能手表、智能手环、智能眼镜、智能手机……随着科技的进步&#xff0c;越来越多的移动与可穿戴智能设备开始走进我们的生活&#xff0c;智能设备在给人们生活带来便利的同时&#xff0c;也带来了一些困惑&#xff0c;越来越多诸如手环、TWS耳机等智能穿戴设备&…

火热的元宇宙,成为未来趋势

近年来&#xff0c;中国在算力上突飞猛进&#xff0c;有望成为世界顶尖&#xff0c;再加数据和算法上的优势&#xff0c;中国就很有可能在元宇宙方面率先开发出原创性的技术&#xff0c;从而实现从“0”到“1”的突破。 元宇宙办公 在未来的元宇宙畅想中&#xff0c;人们不仅…

java Lambda表达式的标准格式及其前提带有(代码演示)

观看本文 首先 你要对Lambda的概念有个基本了解 对此 您可以先查看我的文章 java Lambda概念 通过实现线程简单体验一下Lambda表达式 跟着上一篇文章做 你的代码会是这样 new Thread( () ->{System.out.println("执行线程"); } ).start();而其中Lambda 表达式 则…

资产种类多数量大、使用地点分散?集中管理,一招搞定

随着银行规模不断壮大&#xff0c;资产数量也随之不断增加&#xff0c;同时银行资产具有总量大、价值高、使用地点分散、管理难度大的特点&#xff0c;IT资产、房产、办公用品、维修保养需求随着业务的快速增长对管理工作带来了压力。 传统资产管理4大痛点 01.账实不符 实物账…