微服务架构的核心是将单体应用拆分为多个独立部署、自治的服务单元,而服务之间的通信依赖于准确的服务地址管理。在动态扩缩容、实例故障漂移、多环境部署的常态化场景下,人工维护服务地址的方式完全不可行,服务注册发现组件应运而生,成为微服务架构的“神经中枢”。本文将从底层原理出发,深度拆解两款主流服务注册发现组件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 全维度核心对比
| 对比维度 | Eureka | Nacos |
| 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胜在功能丰富、性能强劲、一站式解决方案,适合新建项目和企业级微服务架构。选择适配业务场景的组件,结合生产最佳实践,才能构建稳定、高效、高可用的微服务体系。