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

简介: 本文深度解析微服务注册发现核心原理,对比Eureka(AP优先、简单稳定)与Nacos(AP/CP双模、功能丰富、性能更强)的架构、机制与适用场景,涵盖6大核心能力、集群同步、健康检查、服务发现模式等,并提供生产级代码实践与选型避坑指南。

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

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

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

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

  1. 服务注册:服务启动时,将自身的元数据(IP、端口、服务名、集群信息、权重、健康状态等)上报到注册中心,完成服务入驻。
  2. 服务续约:服务运行期间,定时向注册中心发送心跳,证明自身存活,避免被注册中心剔除。
  3. 服务发现:服务消费者从注册中心获取目标服务的实例列表,本地缓存后用于RPC调用,避免每次调用都请求注册中心。
  4. 健康检查:注册中心持续校验服务实例的存活状态,剔除不可用实例,保证服务列表的准确性。
  5. 服务下线:服务正常关闭时,主动通知注册中心,将自身从服务列表中移除,避免消费者调用到无效实例。
  6. 集群同步:注册中心集群节点之间的数据同步,保证多节点之间的数据一致性,实现注册中心的高可用。

二、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节点。

同步流程:

  1. 客户端向某个Eureka Server节点发送注册/续约/下线请求
  2. 该节点完成本地存储更新后,将事件封装成ReplicationTask,放入异步同步队列
  3. 后台线程异步将任务同步到其他所有peer节点
  4. 其他节点接收到同步任务后,更新本地的实例信息

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

2.4 Eureka 服务发现机制

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

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

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

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

3.1 Nacos 核心架构

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

架构分层说明:

  1. 核心API层:对外提供HTTP/gRPC接口,接收客户端的注册、发现、配置管理等请求,2.x版本默认使用gRPC协议,大幅提升了传输性能。
  2. 业务模块层:分为Naming Service(服务发现)和Config Service(配置管理)两大核心模块,本文重点讲解Naming Service模块。
  3. 一致性协议层:提供两种一致性协议,Distro协议(AP模式)和Raft协议(CP模式),满足不同场景的一致性需求。
  4. 存储层:本地内存存储+MySQL持久化存储,内存存储保证服务实例查询的高性能,MySQL存储保证持久化数据不丢失。

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

3.2 Nacos 核心数据模型

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

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

每个层级的核心作用:

  1. 命名空间:最顶层的隔离单元,用于多租户、多环境隔离,比如dev、test、prod环境分别创建不同的命名空间,不同命名空间之间的数据完全隔离,默认命名空间为public。
  2. 分组:服务的二级隔离单元,用于对服务进行分组管理,比如同一个命名空间下,分为电商组、支付组、物流组,不同分组的服务名可以重复,默认分组为DEFAULT_GROUP。
  3. 服务:服务的唯一标识,对应业务中的一个微服务,比如user-service、order-service。
  4. 集群:服务的实例集群划分,比如同一个服务下,分为上海集群、北京集群,实现同机房优先调用,降低网络延迟,默认集群为DEFAULT。
  5. 实例:服务的具体节点,包含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的场景

  1. 存量Spring Cloud Netflix技术栈项目,已经深度集成Eureka,业务稳定,无额外功能需求,不需要大规模重构。
  2. 业务场景对服务注册发现的要求仅为基础的地址管理,不需要配置管理、流量管理等额外功能,追求极简部署和运维。
  3. 部署环境资源有限,无法提供MySQL等外部依赖,仅需单JDK环境即可运行。
  4. 业务对可用性要求极高,允许短暂的服务列表不一致,完全不能接受注册中心集群不可用的场景。

5.2 优先选择Nacos的场景

  1. 新建微服务项目,技术栈选型Spring Cloud Alibaba,追求一站式的微服务解决方案,减少组件依赖。
  2. 业务需要多环境、多租户、多集群的精细化服务管理,比如大型企业级项目,多团队协作开发。
  3. 服务实例规模大,单集群实例数超过1w,需要更高的性能和更低的延迟。
  4. 业务需要一体化的配置管理能力,不想同时维护注册中心和配置中心两套组件。
  5. 业务需要精细化的流量管理能力,比如权重路由、灰度发布、同机房优先调用等。
  6. 项目有国产化适配要求,需要国产开源组件,中文文档和社区支持完善。

5.3 选型避坑核心提醒

  1. Eureka的自我保护机制在生产环境需要根据场景调整阈值,避免网络波动触发后,大量无效实例被保留,导致消费者调用失败。
  2. Nacos的实例类型选择是核心,常规微服务实例必须使用临时实例,不要使用持久化实例,否则会导致Raft协议的写性能瓶颈,同时实例下线后不会被自动剔除。
  3. Eureka集群节点数建议3-5个,过多的节点会导致集群同步压力过大,数据不一致的时间变长。
  4. 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 生产环境最佳实践

  1. 集群节点数控制在3-5个,避免过多节点导致同步压力过大,同时保证高可用。
  2. 自我保护机制的阈值根据实际业务场景调整,大规模实例集群可适当降低阈值,避免频繁触发。
  3. 核心业务可缩短续约间隔到10秒,过期时间到30秒,加快故障感知速度。
  4. 开启健康检查端点,配置eureka.client.healthcheck.enabled=true,让Eureka根据应用的健康状态更新实例状态,而非仅靠心跳。
  5. 生产环境配置合适的日志级别,避免DEBUG日志过多导致磁盘IO压力过大。
  6. 集群节点配置hosts解析,避免网络波动导致节点间通信失败。

7.2 Nacos 生产环境最佳实践

  1. 实例类型严格区分:常规微服务实例必须使用临时实例,仅固定地址的中间件、数据库使用持久化实例。
  2. 集群部署规范:AP模式至少3个节点,CP模式必须使用奇数个节点,生产环境建议3个节点起步,大规模集群建议5个节点。
  3. 持久化配置:生产环境必须使用MySQL集群作为持久化存储,避免单机存储故障导致数据丢失。
  4. 命名空间规划:严格按照环境划分命名空间,dev、test、prod完全隔离,避免跨环境调用。
  5. 健康检查配置:临时实例使用默认的心跳模式,持久化实例根据服务类型选择合适的探测方式。
  6. 权限控制:生产环境必须开启Nacos的权限控制,为不同的团队分配不同的命名空间权限,避免误操作。
  7. 性能调优:大规模实例集群,调整JVM堆内存到4G以上,同时调整Distro协议的同步参数,提升同步效率。
  8. 版本选择:生产环境必须使用稳定版,定期更新小版本修复安全漏洞。

7.3 通用避坑指南

  1. 避免注册中心和业务服务部署在同一台机器,防止业务服务的资源占用导致注册中心不可用。
  2. 生产环境必须配置注册中心集群,避免单点故障。
  3. 消费者必须本地缓存服务实例列表,同时设置合理的缓存刷新时间,保证服务变更的实时性。
  4. 服务调用必须配置超时时间、重试机制、熔断降级,避免注册中心故障导致服务调用雪崩。
  5. 注册中心的监控告警必须完善,监控集群节点的健康状态、CPU、内存、磁盘IO、请求量、实例数等指标,设置合理的告警阈值。

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

目录
相关文章
|
10天前
|
人工智能 安全 Linux
【OpenClaw保姆级图文教程】阿里云/本地部署集成模型Ollama/Qwen3.5/百炼 API 步骤流程及避坑指南
2026年,AI代理工具的部署逻辑已从“单一云端依赖”转向“云端+本地双轨模式”。OpenClaw(曾用名Clawdbot)作为开源AI代理框架,既支持对接阿里云百炼等云端免费API,也能通过Ollama部署本地大模型,完美解决两类核心需求:一是担心云端API泄露核心数据的隐私安全诉求;二是频繁调用导致token消耗过高的成本控制需求。
5495 13
|
18天前
|
人工智能 JavaScript Ubuntu
5分钟上手龙虾AI!OpenClaw部署(阿里云+本地)+ 免费多模型配置保姆级教程(MiniMax、Claude、阿里云百炼)
OpenClaw(昵称“龙虾AI”)作为2026年热门的开源个人AI助手,由PSPDFKit创始人Peter Steinberger开发,核心优势在于“真正执行任务”——不仅能聊天互动,还能自动处理邮件、管理日程、订机票、写代码等,且所有数据本地处理,隐私完全可控。它支持接入MiniMax、Claude、GPT等多类大模型,兼容微信、Telegram、飞书等主流聊天工具,搭配100+可扩展技能,成为兼顾实用性与隐私性的AI工具首选。
21830 117
|
14天前
|
人工智能 安全 前端开发
Team 版 OpenClaw:HiClaw 开源,5 分钟完成本地安装
HiClaw 基于 OpenClaw、Higress AI Gateway、Element IM 客户端+Tuwunel IM 服务器(均基于 Matrix 实时通信协议)、MinIO 共享文件系统打造。
8304 8

热门文章

最新文章