欢迎光临
我们一直在努力

服务注册发现深度拆解:Nacos vs Eureka 核心原理、架构选型与生产落地

微服务架构的核心是将单体应用拆分为多个独立部署、自治的服务单元,而服务之间的通信依赖于准确的服务地址管理。在动态扩缩容、实例故障漂移、多环境部署的常态化场景下,人工维护服务地址的方式完全不可行,服务注册发现组件应运而生,成为微服务架构的“神经中枢”。本文将从底层原理出发,深度拆解两款主流服务注册发现组件Eureka与Nacos的核心架构、运行机制,通过全维度对比给出清晰的选型决策指南,并提供生产级代码实践。

一、服务注册发现的核心本质与核心能力

服务注册发现的本质是服务元数据的分布式存储与动态分发系统,核心解决“服务在哪里”的问题,同时提供健康状态校验、流量管理、故障隔离的基础能力,是微服务架构中服务通信的基石。

其核心能力可拆解为6个核心环节:

  • 服务注册:服务启动时,将自身的元数据(IP、端口、服务名、集群信息、权重、健康状态等)上报到注册中心,完成服务入驻。

  • 服务续约:服务运行期间,定时向注册中心发送心跳,证明自身存活,避免被注册中心剔除。

  • 服务发现:服务消费者从注册中心获取目标服务的实例列表,本地缓存后用于RPC调用,避免每次调用都请求注册中心。

  • 健康检查:注册中心持续校验服务实例的存活状态,剔除不可用实例,保证服务列表的准确性。

  • 服务下线:服务正常关闭时,主动通知注册中心,将自身从服务列表中移除,避免消费者调用到无效实例。

  • 集群同步:注册中心集群节点之间的数据同步,保证多节点之间的数据一致性,实现注册中心的高可用。

  • 二、Eureka 核心原理与架构深度解析

    Eureka是Netflix开源的服务注册发现组件,是Spring Cloud Netflix生态的核心组件之一,专为AWS云环境设计,主打高可用性和弹性伸缩。目前Eureka 2.x版本已停止开发,1.x版本处于维护状态。

    2.1 Eureka 核心架构

    Eureka采用纯Java开发,基于对等节点的分布式架构设计,无中心化主从节点,所有节点地位平等。

    架构核心组件分为两类:

    • Eureka Server:注册中心服务端,负责接收服务注册、续约、下线请求,维护服务实例列表,集群节点之间对等复制数据,对外提供查询能力。

    • Eureka Client:客户端组件,集成在服务提供者和消费者中,负责向Server完成注册、续约、下线操作,同时定时从Server拉取服务实例列表并本地缓存,支撑服务调用。

    Eureka的核心设计理念是AP优先,基于CAP理论,Eureka在网络分区等异常场景下,优先保证服务的可用性,放弃强一致性。即使集群节点之间数据不一致,每个Eureka Server依然可以接收注册和查询请求,不会拒绝客户端请求,避免整个注册中心瘫痪。

    2.2 Eureka 核心运行机制

    2.2.1 服务注册机制

    服务启动时,Eureka Client会向配置的Eureka Server发送POST请求,携带自身的全量元数据信息。Eureka Server接收到请求后,将实例信息存储到本地的ConcurrentHashMap中,以服务名作为key,实例列表作为value。同时,Server会将注册事件同步到集群中的其他peer节点,完成集群数据同步。

    客户端注册失败会自动重试,默认重试3次,间隔1秒,保证注册成功率。

    2.2.2 服务续约机制(心跳)

    服务注册成功后,Eureka Client会每隔30秒(默认)向Eureka Server发送一次PUT请求,即心跳续约,证明自身存活。Eureka Server接收到续约请求后,会更新该实例的最后续约时间,标记实例为存活状态。

    核心配置参数:

    • eureka.instance.lease-renewal-interval-in-seconds:续约间隔,默认30秒

    • eureka.instance.lease-expiration-duration-in-seconds:租约过期时间,默认90秒,即Server超过90秒未收到心跳,会判定实例失效

    2.2.3 服务下线机制

    服务正常关闭时,Eureka Client会向Eureka Server发送DELETE请求,通知Server下线自身实例。Server接收到请求后,将该实例从服务列表中移除,并同步下线事件到集群其他节点,同时通知订阅该服务的客户端更新实例列表。

    2.2.4 服务剔除机制

    Eureka Server会开启一个定时任务,每隔60秒(默认)执行一次,遍历所有服务实例,检查实例的最后续约时间是否超过租约过期时间。如果超过阈值,就将该实例标记为失效,从服务列表中剔除,避免消费者调用到不可用实例。

    2.2.5 自我保护机制

    自我保护机制是Eureka最核心的特性之一,也是生产环境最容易踩坑的设计。 设计背景:在网络分区场景下,Eureka Server和大量客户端之间的网络不通,导致大量实例心跳失败,此时如果Eureka Server剔除所有这些实例,会导致消费者本地缓存的实例列表被清空,无法调用任何服务,即使这些实例在网络分区的另一边是正常运行的,完全违背了Eureka的AP设计理念。

    触发条件:Eureka Server会统计每分钟收到的客户端续约总数,与预期的续约总数对比,如果实际收到的续约数占预期的比例低于阈值(默认0.85,即85%),且持续15分钟,就会触发自我保护机制。

    触发后的行为:Eureka Server不会再剔除任何服务实例,即使实例的租约已经过期,会保留所有的注册信息,直到网络恢复,续约比例恢复到阈值以上,才会退出自我保护机制。

    2.3 Eureka 集群数据同步机制

    Eureka Server集群采用对等复制(Peer-to-Peer) 架构,无主从节点,所有节点都可以接收客户端的写请求,然后将写事件同步到其他所有peer节点。

    同步流程:

  • 客户端向某个Eureka Server节点发送注册/续约/下线请求

  • 该节点完成本地存储更新后,将事件封装成ReplicationTask,放入异步同步队列

  • 后台线程异步将任务同步到其他所有peer节点

  • 其他节点接收到同步任务后,更新本地的实例信息

  • 为了避免同步死循环,Eureka Server在同步事件时会带上节点标识,如果收到的同步事件是自身发出的,会直接忽略,不会再次同步。同步过程为异步执行,集群节点之间的数据会有短暂的不一致,最终达到一致状态。

    2.4 Eureka 服务发现机制

    Eureka Client(消费者)会每隔30秒(默认)向Eureka Server发送GET请求,拉取全量的服务实例列表,更新本地缓存。如果本地缓存已有数据,请求会带上版本号,Server对比版本号后如果没有变化,会返回304状态码,客户端继续使用本地缓存,减少网络传输开销。

    消费者发起RPC调用时,直接从本地缓存中获取目标服务的实例列表,通过负载均衡算法选择一个实例发起调用,不会每次调用都请求注册中心,保证了调用性能。

    三、Nacos 核心原理与架构深度解析

    Nacos是阿里巴巴开源的动态服务发现、配置管理和服务管理平台,是Spring Cloud Alibaba生态的核心组件,专为云原生微服务架构设计,提供了比Eureka更丰富的功能和更高的性能。

    3.1 Nacos 核心架构

    Nacos采用分层架构设计,除了服务发现能力外,还内置了配置管理、流量管理等能力,是一站式的微服务管理组件。

    架构分层说明:

  • 核心API层:对外提供HTTP/gRPC接口,接收客户端的注册、发现、配置管理等请求,2.x版本默认使用gRPC协议,大幅提升了传输性能。

  • 业务模块层:分为Naming Service(服务发现)和Config Service(配置管理)两大核心模块,本文重点讲解Naming Service模块。

  • 一致性协议层:提供两种一致性协议,Distro协议(AP模式)和Raft协议(CP模式),满足不同场景的一致性需求。

  • 存储层:本地内存存储+MySQL持久化存储,内存存储保证服务实例查询的高性能,MySQL存储保证持久化数据不丢失。

  • Nacos的核心设计理念是AP与CP双模式支持,用户可以根据业务场景选择对应的模式,同时兼顾可用性和一致性,提供了比Eureka更灵活的选择。

    3.2 Nacos 核心数据模型

    Nacos采用了分级的服务元数据模型,比Eureka的扁平模型更灵活,支持多环境、多租户、多集群的精细化管理,模型层级如下:

    命名空间(Namespace)→ 分组(Group)→ 服务(Service)→ 集群(Cluster)→ 实例(Instance)

    每个层级的核心作用:

  • 命名空间:最顶层的隔离单元,用于多租户、多环境隔离,比如dev、test、prod环境分别创建不同的命名空间,不同命名空间之间的数据完全隔离,默认命名空间为public。

  • 分组:服务的二级隔离单元,用于对服务进行分组管理,比如同一个命名空间下,分为电商组、支付组、物流组,不同分组的服务名可以重复,默认分组为DEFAULT_GROUP。

  • 服务:服务的唯一标识,对应业务中的一个微服务,比如user-service、order-service。

  • 集群:服务的实例集群划分,比如同一个服务下,分为上海集群、北京集群,实现同机房优先调用,降低网络延迟,默认集群为DEFAULT。

  • 实例:服务的具体节点,包含IP、端口、权重、健康状态、元数据等信息。

  • 同时,Nacos将实例分为两种核心类型,这是与Eureka最核心的差异之一:

    • 临时实例(Ephemeral Instance):默认类型,对应AP模式,客户端主动上报心跳,服务端不持久化实例数据,仅存储在内存中,客户端失联后会被自动剔除,适合常规的微服务实例、动态扩缩容频繁的场景。

    • 持久化实例(Persistent Instance):对应CP模式,服务端主动探测健康状态,实例数据会持久化到MySQL中,即使客户端全部下线,实例信息依然会保留,适合数据库、中间件等固定地址的服务实例。

    3.3 Nacos 核心运行机制

    3.3.1 服务注册机制

    服务启动时,Nacos Client会向Nacos Server发送注册请求,携带命名空间、分组、服务名、集群、实例元数据、实例类型等信息。

    • 临时实例:注册请求通过Distro协议处理,Server将实例信息存储到本地内存中,同时同步到集群中的其他节点,立即返回注册成功响应。

    • 持久化实例:注册请求通过Raft协议处理,必须集群中超过半数的节点写入成功,才会返回注册成功响应,保证强一致性,同时将实例数据持久化到MySQL中。

    3.3.2 健康检查机制

    Nacos提供了两种健康检查模式,分别对应不同的实例类型:

    • 客户端心跳上报模式(临时实例):客户端每隔5秒(默认)向Server发送一次心跳请求,更新实例的最后心跳时间。Server如果15秒未收到心跳,会将实例标记为不健康;如果30秒未收到心跳,会将实例从服务列表中剔除。

    • 服务端主动探测模式(持久化实例):Server会定时向实例发起健康检查,支持TCP、HTTP、MySQL等多种探测方式,默认TCP探测。如果探测失败,会将实例标记为不健康,不会直接剔除,直到实例恢复正常。

    3.3.3 服务下线机制
    • 主动下线:服务正常关闭时,Nacos Client会向Server发送下线请求,Server将实例从服务列表中移除,同步到集群其他节点,并推送变更事件给订阅了该服务的消费者。

    • 自动下线:临时实例心跳超时30秒,Server自动剔除实例,同步集群并推送变更事件。

    3.3.4 服务发现与订阅推送机制

    这是Nacos和Eureka最大的性能差异点之一。Nacos采用推拉结合的订阅模式,而Eureka仅支持客户端定时拉取模式。

    • 拉取模式:客户端启动时,会向Server拉取目标服务的全量实例列表,本地缓存。

    • 推送模式:客户端会和Server建立gRPC长连接,订阅目标服务的变更事件。当服务的实例列表发生变化(注册、下线、健康状态变化)时,Server会立即通过长连接将增量变更事件推送给所有订阅的客户端,客户端实时更新本地缓存。

    这种模式的优势:客户端不需要定时全量拉取,大幅减少了网络IO和CPU开销,同时服务变更的感知延迟从Eureka的30秒降低到秒级甚至毫秒级,实时性大幅提升。

    3.4 Nacos 集群一致性协议

    Nacos的核心竞争力之一就是支持两种一致性协议,分别对应不同的业务场景:

    3.4.1 Distro协议(AP模式)

    Distro是阿里自研的最终一致性协议,专为临时实例的服务发现场景设计,核心设计理念是分区负责、对等同步、最终一致。

    核心原理:

    • 集群中的每个节点负责一部分服务的写入请求,根据服务名的hash值分配到对应的节点

    • 每个节点将自己负责的服务数据同步到集群中的其他所有节点,保证全集群的数据最终一致

    • 每个节点都可以处理所有的读请求,即使节点之间数据不一致,依然可以对外提供服务,保证可用性

    • 数据仅存储在内存中,不持久化到MySQL,节点重启后会从其他节点同步全量数据

    适用场景:常规的微服务临时实例,需要高可用性,允许短暂的数据不一致。

    3.4.2 Raft协议(CP模式)

    Raft是业界通用的强一致性分布式共识协议,Nacos用它来处理持久化实例的注册请求和配置管理数据。

    核心原理:

    • 集群中会选举出一个Leader节点,所有的写请求都必须发送到Leader节点,Leader节点处理后,将数据同步到Follower节点

    • 只有当集群中超过半数的Follower节点写入成功,Leader才会提交数据,返回客户端写入成功,保证强一致性

    • 如果Leader节点故障,集群会重新选举新的Leader,选举期间集群无法处理写请求,保证数据一致性,牺牲了短暂的可用性

    • 数据会持久化到MySQL中,保证节点重启后数据不丢失

    适用场景:持久化实例、配置管理等需要强一致性的场景,不允许数据不一致。

    四、Nacos vs Eureka 全维度核心对比

    对比维度EurekaNacos
    CAP模型 仅支持AP模式,优先可用性,最终一致性 双模式支持,临时实例AP模式,持久化实例CP模式
    健康检查机制 仅支持客户端主动心跳上报 支持客户端心跳上报、服务端TCP/HTTP/MySQL主动探测
    集群同步机制 Peer-to-Peer对等异步复制,最终一致 AP模式用Distro协议分区对等同步,CP模式用Raft协议强一致同步
    服务订阅模式 客户端定时全量拉取(默认30秒),实时性差 推拉结合,gRPC长连接增量推送,实时性毫秒级
    数据模型 扁平模型,仅服务名+实例,隔离能力弱 分级模型:命名空间→分组→服务→集群→实例,精细化多维度隔离
    单机性能 单机最大支持1w+服务实例,吞吐量较低 2.x版本单机支持10w+服务实例,吞吐量是Eureka的10倍以上
    功能丰富度 仅提供基础的服务注册发现能力 服务注册发现、配置管理、流量管理、权重路由、熔断降级、灰度发布等一体化能力
    负载均衡能力 仅依赖客户端负载均衡组件 内置服务端权重配置、同机房优先、就近路由等负载均衡策略
    社区活跃度 1.x处于维护状态,2.x已停更,无新特性迭代 阿里持续维护迭代,社区活跃,国内用户基数大,问题响应快
    生态适配 原生适配Spring Cloud Netflix生态 原生适配Spring Cloud Alibaba、Dubbo、K8s等主流微服务生态
    运维成本 部署简单,仅依赖JDK,无外部依赖 单机部署简单,集群持久化部署依赖MySQL,运维复杂度略高
    国内适配 无特殊适配,国内网络环境下镜像拉取、问题排查不便 国产开源,中文文档完善,适配国内云环境,国产化支持好

    五、架构选型决策指南

    5.1 优先选择Eureka的场景

  • 存量Spring Cloud Netflix技术栈项目,已经深度集成Eureka,业务稳定,无额外功能需求,不需要大规模重构。

  • 业务场景对服务注册发现的要求仅为基础的地址管理,不需要配置管理、流量管理等额外功能,追求极简部署和运维。

  • 部署环境资源有限,无法提供MySQL等外部依赖,仅需单JDK环境即可运行。

  • 业务对可用性要求极高,允许短暂的服务列表不一致,完全不能接受注册中心集群不可用的场景。

  • 5.2 优先选择Nacos的场景

  • 新建微服务项目,技术栈选型Spring Cloud Alibaba,追求一站式的微服务解决方案,减少组件依赖。

  • 业务需要多环境、多租户、多集群的精细化服务管理,比如大型企业级项目,多团队协作开发。

  • 服务实例规模大,单集群实例数超过1w,需要更高的性能和更低的延迟。

  • 业务需要一体化的配置管理能力,不想同时维护注册中心和配置中心两套组件。

  • 业务需要精细化的流量管理能力,比如权重路由、灰度发布、同机房优先调用等。

  • 项目有国产化适配要求,需要国产开源组件,中文文档和社区支持完善。

  • 5.3 选型避坑核心提醒

  • Eureka的自我保护机制在生产环境需要根据场景调整阈值,避免网络波动触发后,大量无效实例被保留,导致消费者调用失败。

  • Nacos的实例类型选择是核心,常规微服务实例必须使用临时实例,不要使用持久化实例,否则会导致Raft协议的写性能瓶颈,同时实例下线后不会被自动剔除。

  • Eureka集群节点数建议3-5个,过多的节点会导致集群同步压力过大,数据不一致的时间变长。

  • Nacos集群部署时,AP模式至少3个节点,CP模式必须使用奇数个节点(3/5/7个),保证Raft协议的选举正常。

  • 六、生产级落地代码实践

    统一环境说明:

    • JDK版本:JDK 17

    • Spring Boot版本:3.2.5

    • Spring Cloud版本:2023.0.2

    • 项目管理:Maven

    6.1 Eureka 落地实践

    分为三个模块:eureka-server(注册中心)、eureka-provider(服务提供者)、eureka-consumer(服务消费者)

    6.1.1 Eureka Server 集群搭建

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>3.2.5</version>
            <relativePath/>
        </parent>
        <groupId>com.jam.demo</groupId>
        <artifactId>eureka-server</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>eureka-server</name>
        <properties>
            <java.version>17</java.version>
            <spring-cloud.version>2023.0.2</spring-cloud.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.32</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>

    application.yml

    spring:
      application:
        name: eureka-server
      profiles:
        active: node1


    spring:
      config:
        activate:
          on-profile: node1
    server:
      port: 8761
    eureka:
      instance:
        hostname: eureka-node1
        prefer-ip-address: true
      client:
        register-with-eureka: true
        fetch-registry: true
        service-url:
          defaultZone: http://eureka-node2:8762/eureka/,http://eureka-node3:8763/eureka/
      server:
        renewal-percent-threshold: 0.85
        enable-self-preservation: true


    spring:
      config:
        activate:
          on-profile: node2
    server:
      port: 8762
    eureka:
      instance:
        hostname: eureka-node2
        prefer-ip-address: true
      client:
        register-with-eureka: true
        fetch-registry: true
        service-url:
          defaultZone: http://eureka-node1:8761/eureka/,http://eureka-node3:8763/eureka/
      server:
        renewal-percent-threshold: 0.85
        enable-self-preservation: true


    spring:
      config:
        activate:
          on-profile: node3
    server:
      port: 8763
    eureka:
      instance:
        hostname: eureka-node3
        prefer-ip-address: true
      client:
        register-with-eureka: true
        fetch-registry: true
        service-url:
          defaultZone: http://eureka-node1:8761/eureka/,http://eureka-node2:8762/eureka/
      server:
        renewal-percent-threshold: 0.85
        enable-self-preservation: true

    EurekaServerApplication.java

    package com.jam.demo.eureka;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

    /**
     * Eureka注册中心服务端启动类
     *
     * @author ken
     */
    @EnableEurekaServer
    @SpringBootApplication
    public class EurekaServerApplication {

        public static void main(String[] args) {
            SpringApplication.run(EurekaServerApplication.class, args);
        }

    }

    6.1.2 Eureka 服务提供者开发

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>3.2.5</version>
            <relativePath/>
        </parent>
        <groupId>com.jam.demo</groupId>
        <artifactId>eureka-provider</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>eureka-provider</name>
        <properties>
            <java.version>17</java.version>
            <spring-cloud.version>2023.0.2</spring-cloud.version>
            <mybatis-plus.version>3.5.7</mybatis-plus.version>
            <mysql.version>8.4.0</mysql.version>
            <guava.version>33.2.0-jre</guava.version>
            <fastjson2.version>2.0.52</fastjson2.version>
            <springdoc.version>2.5.0</springdoc.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springdoc</groupId>
                <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
                <version>${springdoc.version}</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>
            <dependency>
                <groupId>com.mysql</groupId>
                <artifactId>mysql-connector-j</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>${guava.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.fastjson2</groupId>
                <artifactId>fastjson2</artifactId>
                <version>${fastjson2.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.32</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>

    application.yml

    spring:
      application:
        name: user-service-provider
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/jam_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
        username: root
        password: root
    server:
      port: 8080
    eureka:
      instance:
        prefer-ip-address: true
        lease-renewal-interval-in-seconds: 30
        lease-expiration-duration-in-seconds: 90
      client:
        register-with-eureka: true
        fetch-registry: true
        service-url:
          defaultZone: http://127.0.0.1:8761/eureka/,http://127.0.0.1:8762/eureka/,http://127.0.0.1:8763/eureka/
    mybatis-plus:
      configuration:
        map-underscore-to-camel-case: true
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      global-config:
        db-config:
          id-type: auto
    springdoc:
      swagger-ui:
        path: /swagger-ui.html
      api-docs:
        path: /v3/api-docs

    MySQL建表语句

    CREATE TABLE IF NOT EXISTS `t_user` (
      `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '用户ID',
      `username` VARCHAR(64) NOT NULL COMMENT '用户名',
      `age` INT DEFAULT NULL COMMENT '年龄',
      `email` VARCHAR(128) DEFAULT NULL COMMENT '邮箱',
      PRIMARY KEY (`id`),
      UNIQUE KEY `uk_username` (`username`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';

    User.java

    package com.jam.demo.provider.entity;

    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import io.swagger.v3.oas.annotations.media.Schema;
    import lombok.Data;

    import java.io.Serializable;

    /**
     * 用户实体类
     *
     * @author ken
     */
    @Data
    @TableName("t_user")
    @Schema(description = "用户实体")
    public class User implements Serializable {

        private static final long serialVersionUID = 1L;

        @TableId(type = IdType.AUTO)
        @Schema(description = "用户ID", example = "1")
        private Long id;

        @Schema(description = "用户名", example = "zhangsan")
        private String username;

        @Schema(description = "年龄", example = "20")
        private Integer age;

        @Schema(description = "邮箱", example = "zhangsan@example.com")
        private String email;
    }

    UserMapper.java

    package com.jam.demo.provider.mapper;

    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.jam.demo.provider.entity.User;
    import org.apache.ibatis.annotations.Mapper;

    /**
     * 用户Mapper接口
     *
     * @author ken
     */
    @Mapper
    public interface UserMapper extends BaseMapper<User> {
    }

    UserService.java

    package com.jam.demo.provider.service;

    import com.baomidou.mybatisplus.extension.service.IService;
    import com.jam.demo.provider.entity.User;

    import java.util.List;

    /**
     * 用户服务接口
     *
     * @author ken
     */
    public interface UserService extends IService<User> {

        /**
         * 根据ID查询用户
         *
         * @param id 用户ID
         * @return 用户实体
         */
        User getUserById(Long id);

        /**
         * 查询所有用户
         *
         * @return 用户列表
         */
        List<User> listAllUsers();
    }

    UserServiceImpl.java

    package com.jam.demo.provider.service.impl;

    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.jam.demo.provider.entity.User;
    import com.jam.demo.provider.mapper.UserMapper;
    import com.jam.demo.provider.service.UserService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    import org.springframework.util.ObjectUtils;

    import java.util.List;

    /**
     * 用户服务实现类
     *
     * @author ken
     */
    @Slf4j
    @Service
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

        @Override
        public User getUserById(Long id) {
            if (ObjectUtils.isEmpty(id)) {
                log.warn("查询用户失败,用户ID为空");
                return null;
            }
            return this.getById(id);
        }

        @Override
        public List<User> listAllUsers() {
            return this.list();
        }
    }

    UserController.java

    package com.jam.demo.provider.controller;

    import com.jam.demo.provider.entity.User;
    import com.jam.demo.provider.service.UserService;
    import io.swagger.v3.oas.annotations.Operation;
    import io.swagger.v3.oas.annotations.Parameter;
    import io.swagger.v3.oas.annotations.tags.Tag;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.util.ObjectUtils;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    import java.util.List;

    /**
     * 用户服务控制器
     *
     * @author ken
     */
    @Slf4j
    @RestController
    @RequestMapping("/user")
    @RequiredArgsConstructor
    @Tag(name = "用户管理", description = "用户相关操作接口")
    public class UserController {

        private final UserService userService;

        @GetMapping("/{id}")
        @Operation(summary = "根据ID查询用户", description = "传入用户ID,返回对应用户信息")
        public User getUserById(
                @Parameter(description = "用户ID", required = true, example = "1")
                @PathVariable Long id) {
            log.info("收到查询用户请求,用户ID:{}", id);
            if (ObjectUtils.isEmpty(id)) {
                log.warn("用户ID为空,查询失败");
                return null;
            }
            return userService.getUserById(id);
        }

        @GetMapping("/list")
        @Operation(summary = "查询所有用户", description = "返回所有用户列表")
        public List<User> listAllUsers() {
            log.info("收到查询所有用户请求");
            return userService.listAllUsers();
        }
    }

    ProviderApplication.java

    package com.jam.demo.provider;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

    /**
     * 服务提供者启动类
     *
     * @author ken
     */
    @EnableDiscoveryClient
    @SpringBootApplication
    public class ProviderApplication {

        public static void main(String[] args) {
            SpringApplication.run(ProviderApplication.class, args);
        }

    }

    6.1.3 Eureka 服务消费者开发

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>3.2.5</version>
            <relativePath/>
        </parent>
        <groupId>com.jam.demo</groupId>
        <artifactId>eureka-consumer</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>eureka-consumer</name>
        <properties>
            <java.version>17</java.version>
            <spring-cloud.version>2023.0.2</spring-cloud.version>
            <guava.version>33.2.0-jre</guava.version>
            <fastjson2.version>2.0.52</fastjson2.version>
            <springdoc.version>2.5.0</springdoc.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springdoc</groupId>
                <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
                <version>${springdoc.version}</version>
            </dependency>
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>${guava.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.fastjson2</groupId>
                <artifactId>fastjson2</artifactId>
                <version>${fastjson2.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.32</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>

    application.yml

    spring:
      application:
        name: order-service-consumer
    server:
      port: 9090
    eureka:
      instance:
        prefer-ip-address: true
      client:
        register-with-eureka: true
        fetch-registry: true
        service-url:
          defaultZone: http://127.0.0.1:8761/eureka/,http://127.0.0.1:8762/eureka/,http://127.0.0.1:8763/eureka/
    springdoc:
      swagger-ui:
        path: /swagger-ui.html
      api-docs:
        path: /v3/api-docs
    feign:
      client:
        config:
          default:
            connect-timeout: 5000
            read-timeout: 10000

    User.java

    package com.jam.demo.consumer.entity;

    import io.swagger.v3.oas.annotations.media.Schema;
    import lombok.Data;

    import java.io.Serializable;

    /**
     * 用户实体类
     *
     * @author ken
     */
    @Data
    @Schema(description = "用户实体")
    public class User implements Serializable {

        private static final long serialVersionUID = 1L;

        @Schema(description = "用户ID", example = "1")
        private Long id;

        @Schema(description = "用户名", example = "zhangsan")
        private String username;

        @Schema(description = "年龄", example = "20")
        private Integer age;

        @Schema(description = "邮箱", example = "zhangsan@example.com")
        private String email;
    }

    UserFeignClient.java

    package com.jam.demo.consumer.feign;

    import com.jam.demo.consumer.entity.User;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;

    import java.util.List;

    /**
     * 用户服务Feign客户端
     *
     * @author ken
     */
    @FeignClient(name = "user-service-provider", path = "/user")
    public interface UserFeignClient {

        @GetMapping("/{id}")
        User getUserById(@PathVariable("id") Long id);

        @GetMapping("/list")
        List<User> listAllUsers();
    }

    OrderController.java

    package com.jam.demo.consumer.controller;

    import com.jam.demo.consumer.entity.User;
    import com.jam.demo.consumer.feign.UserFeignClient;
    import io.swagger.v3.oas.annotations.Operation;
    import io.swagger.v3.oas.annotations.Parameter;
    import io.swagger.v3.oas.annotations.tags.Tag;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.util.ObjectUtils;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    import java.util.List;

    /**
     * 订单服务控制器
     *
     * @author ken
     */
    @Slf4j
    @RestController
    @RequestMapping("/order")
    @RequiredArgsConstructor
    @Tag(name = "订单管理", description = "订单相关操作接口")
    public class OrderController {

        private final UserFeignClient userFeignClient;

        @GetMapping("/user/{id}")
        @Operation(summary = "查询订单对应用户信息", description = "传入用户ID,调用用户服务获取用户信息")
        public User getOrderUser(
                @Parameter(description = "用户ID", required = true, example = "1")
                @PathVariable Long id) {
            log.info("收到查询订单用户请求,用户ID:{}", id);
            if (ObjectUtils.isEmpty(id)) {
                log.warn("用户ID为空,查询失败");
                return null;
            }
            return userFeignClient.getUserById(id);
        }

        @GetMapping("/user/list")
        @Operation(summary = "查询所有用户列表", description = "调用用户服务获取所有用户列表")
        public List<User> listAllUsers() {
            log.info("收到查询所有用户列表请求");
            return userFeignClient.listAllUsers();
        }
    }

    ConsumerApplication.java

    package com.jam.demo.consumer;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.openfeign.EnableFeignClients;

    /**
     * 服务消费者启动类
     *
     * @author ken
     */
    @EnableDiscoveryClient
    @EnableFeignClients
    @SpringBootApplication
    public class ConsumerApplication {

        public static void main(String[] args) {
            SpringApplication.run(ConsumerApplication.class, args);
        }

    }

    6.2 Nacos 落地实践

    分为两个模块:nacos-provider(服务提供者)、nacos-consumer(服务消费者),Nacos Server版本2.4.3

    6.2.1 Nacos 服务提供者开发

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>3.2.5</version>
            <relativePath/>
        </parent>
        <groupId>com.jam.demo</groupId>
        <artifactId>nacos-provider</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>nacos-provider</name>
        <properties>
            <java.version>17</java.version>
            <spring-cloud.version>2023.0.2</spring-cloud.version>
            <spring-cloud-alibaba.version>2023.0.1.2</spring-cloud-alibaba.version>
            <mybatis-plus.version>3.5.7</mybatis-plus.version>
            <mysql.version>8.4.0</mysql.version>
            <guava.version>33.2.0-jre</guava.version>
            <fastjson2.version>2.0.52</fastjson2.version>
            <springdoc.version>2.5.0</springdoc.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springdoc</groupId>
                <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
                <version>${springdoc.version}</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>
            <dependency>
                <groupId>com.mysql</groupId>
                <artifactId>mysql-connector-j</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>${guava.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.fastjson2</groupId>
                <artifactId>fastjson2</artifactId>
                <version>${fastjson2.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.32</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <dependency>
                    <groupId>com.alibaba.cloud</groupId>
                    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                    <version>${spring-cloud-alibaba.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>

    application.yml

    spring:
      application:
        name: user-service
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/jam_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
        username: root
        password: root
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
            namespace: dev
            group: BUSINESS_GROUP
            cluster-name: SH
            ephemeral: true
            weight: 1.0
            metadata:
              version: 1.0.0
              env: dev
    server:
      port: 8080
    mybatis-plus:
      configuration:
        map-underscore-to-camel-case: true
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      global-config:
        db-config:
          id-type: auto
    springdoc:
      swagger-ui:
        path: /swagger-ui.html
      api-docs:
        path: /v3/api-docs

    实体类、Mapper、Service、Controller代码与Eureka服务提供者完全一致,启动类如下:

    NacosProviderApplication.java

    package com.jam.demo.nacos.provider;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

    /**
     * Nacos服务提供者启动类
     *
     * @author ken
     */
    @EnableDiscoveryClient
    @SpringBootApplication
    public class NacosProviderApplication {

        public static void main(String[] args) {
            SpringApplication.run(NacosProviderApplication.class, args);
        }

    }

    6.2.2 Nacos 服务消费者开发

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>3.2.5</version>
            <relativePath/>
        </parent>
        <groupId>com.jam.demo</groupId>
        <artifactId>nacos-consumer</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>nacos-consumer</name>
        <properties>
            <java.version>17</java.version>
            <spring-cloud.version>2023.0.2</spring-cloud.version>
            <spring-cloud-alibaba.version>2023.0.1.2</spring-cloud-alibaba.version>
            <guava.version>33.2.0-jre</guava.version>
            <fastjson2.version>2.0.52</fastjson2.version>
            <springdoc.version>2.5.0</springdoc.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springdoc</groupId>
                <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
                <version>${springdoc.version}</version>
            </dependency>
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>${guava.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.fastjson2</groupId>
                <artifactId>fastjson2</artifactId>
                <version>${fastjson2.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.32</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <dependency>
                    <groupId>com.alibaba.cloud</groupId>
                    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                    <version>${spring-cloud-alibaba.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>

    application.yml

    spring:
      application:
        name: order-service
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
            namespace: dev
            group: BUSINESS_GROUP
            cluster-name: SH
            ephemeral: true
    server:
      port: 9090
    springdoc:
      swagger-ui:
        path: /swagger-ui.html
      api-docs:
        path: /v3/api-docs
    feign:
      client:
        config:
          default:
            connect-timeout: 5000
            read-timeout: 10000
      loadbalancer:
        nacos:
          enabled: true

    实体类、Feign客户端、Controller代码与Eureka服务消费者完全一致,启动类如下:

    NacosConsumerApplication.java

    package com.jam.demo.nacos.consumer;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.openfeign.EnableFeignClients;

    /**
     * Nacos服务消费者启动类
     *
     * @author ken
     */
    @EnableDiscoveryClient
    @EnableFeignClients
    @SpringBootApplication
    public class NacosConsumerApplication {

        public static void main(String[] args) {
            SpringApplication.run(NacosConsumerApplication.class, args);
        }

    }

    七、生产环境最佳实践与避坑指南

    7.1 Eureka 生产环境最佳实践

  • 集群节点数控制在3-5个,避免过多节点导致同步压力过大,同时保证高可用。

  • 自我保护机制的阈值根据实际业务场景调整,大规模实例集群可适当降低阈值,避免频繁触发。

  • 核心业务可缩短续约间隔到10秒,过期时间到30秒,加快故障感知速度。

  • 开启健康检查端点,配置eureka.client.healthcheck.enabled=true,让Eureka根据应用的健康状态更新实例状态,而非仅靠心跳。

  • 生产环境配置合适的日志级别,避免DEBUG日志过多导致磁盘IO压力过大。

  • 集群节点配置hosts解析,避免网络波动导致节点间通信失败。

  • 7.2 Nacos 生产环境最佳实践

  • 实例类型严格区分:常规微服务实例必须使用临时实例,仅固定地址的中间件、数据库使用持久化实例。

  • 集群部署规范:AP模式至少3个节点,CP模式必须使用奇数个节点,生产环境建议3个节点起步,大规模集群建议5个节点。

  • 持久化配置:生产环境必须使用MySQL集群作为持久化存储,避免单机存储故障导致数据丢失。

  • 命名空间规划:严格按照环境划分命名空间,dev、test、prod完全隔离,避免跨环境调用。

  • 健康检查配置:临时实例使用默认的心跳模式,持久化实例根据服务类型选择合适的探测方式。

  • 权限控制:生产环境必须开启Nacos的权限控制,为不同的团队分配不同的命名空间权限,避免误操作。

  • 性能调优:大规模实例集群,调整JVM堆内存到4G以上,同时调整Distro协议的同步参数,提升同步效率。

  • 版本选择:生产环境必须使用稳定版,定期更新小版本修复安全漏洞。

  • 7.3 通用避坑指南

  • 避免注册中心和业务服务部署在同一台机器,防止业务服务的资源占用导致注册中心不可用。

  • 生产环境必须配置注册中心集群,避免单点故障。

  • 消费者必须本地缓存服务实例列表,同时设置合理的缓存刷新时间,保证服务变更的实时性。

  • 服务调用必须配置超时时间、重试机制、熔断降级,避免注册中心故障导致服务调用雪崩。

  • 注册中心的监控告警必须完善,监控集群节点的健康状态、CPU、内存、磁盘IO、请求量、实例数等指标,设置合理的告警阈值。

  • 服务注册发现是微服务架构的核心基础设施,Eureka和Nacos都是经过生产验证的优秀组件,没有绝对的优劣之分,只有适配场景的差异。Eureka胜在极简、稳定、高可用,适合存量Netflix生态项目;Nacos胜在功能丰富、性能强劲、一站式解决方案,适合新建项目和企业级微服务架构。选择适配业务场景的组件,结合生产最佳实践,才能构建稳定、高效、高可用的微服务体系。

    赞(0)
    未经允许不得转载:171主机测评 » 服务注册发现深度拆解:Nacos vs Eureka 核心原理、架构选型与生产落地
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址