《Spring Cloud Eureka 高可用集群实战:从零构建 99.99% 可靠性的微服务注册中心》
从零构建高可用 Eureka 集群 | Spring Cloud 微服务架构深度实践指南
本文核心内容基于《Spring Cloud 微服务架构开发》第1版整理,结合生产级实践经验优化
实验环境:IntelliJ IDEA 2024 | JDK 1.8| Spring Boot 2.1.7.RELEASE | Spring Cloud Greenwich.SR2项目源码:本文完整示例代码已开源至 GitHub,建议结合实践阅读
🔗 https://github.com/hongmengchen/spring-cloud-eureka-ha-demo
前言:为什么需要高可用Eureka集群?
在微服务架构中,注册中心是服务发现与治理的核心枢纽。单节点Eureka Server存在单点故障风险,一旦宕机将导致整个系统服务发现失效。高可用集群通过多节点互备,实现服务注册表同步与故障自动转移,保障系统99.99%的可用性。
本文是《从零到一!Spring Cloud Eureka 微服务注册中心手把手搭建指南》的进阶篇,将带领读者完成以下目标:
- 集群架构设计:基于CAP理论(一致性、可用性、分区容错性)选择AP模式,确保网络波动时仍可提供服务发现。
- 实战搭建:通过双节点互注册实现数据同步,结合Hosts配置模拟多服务器环境。
- 全链路改造:覆盖服务提供者、消费者的集群适配,并验证故障切换能力。
实施路线图
基本流程:
- 系统环境准备(Hosts配置)
- Eureka Server 双节点改造
- 服务提供者集群适配
- 改造服务消费者
- 测试运行
1. 系统环境准备(Hosts配置)
作用:通过域名映射实现本地多节点模拟
以 Windows 系统为例,如果要构建集群,需要修改 hosts 文件,为其添加主机名的映射。
Windows系统操作指南:
- 以管理员身份运行记事本
- 打开
C:\Windows\System32\drivers\etc\hosts
- 添加以下映射规则:
# Eureka 集群节点映射
127.0.0.1 server1 # 主节点域名
127.0.0.1 server2 # 备用节点域名
在修改 hosts 文件时,部分同学可能会遇到无法修改的问题,我另写了一篇博客以解决大家的问题:Windows 系统 hosts 文件无法保存?三步搞定权限设置!
建议:大家在修改 hosts 文件权限之后,建议使用完再改会来,也是为了安全考虑嗷
关键验证命令:
ping server1 # 应返回127.0.0.1
ping server2 # 应返回127.0.0.1
2. Eureka Server 双节点改造
核心逻辑:节点间互相注册形成环形依赖
按照搭建 eureka-server 的方式,再搭建一个名为 eureka-server-another 的 Eureka Server。
eureka-server-another 的 application.yml 配置文件内容如下:
节点2配置(server2:7009)
server:
port: 7009
spring:
application:
name: eurake-server-another
eureka:
client:
fetch-registry: false
register-with-eureka: false
service-url:
defaultZone: http://server1:7000/eureka/ # 指向节点1
instance:
hostname: server2 # 必须与Hosts配置一致
创建项目每次都差不多
接下来就是补齐这个初创项目的框架
- 补充 pom.xml
- 添加目录结构
- 完善配置文件 application.yml
- 写上启动类
- eureka-server-another 和 eureka-server 一样都是 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 http://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>2.1.7.RELEASE</version>
<relativePath/>
</parent>
<!-- 基本信息 -->
<groupId>cn.hmck</groupId>
<artifactId>eureka-server-another</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 属性 -->
<properties>
<!-- jdk版本 -->
<java.version>1.8</java.version>
<!-- spring-cloud版本 -->
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 依赖 -->
<dependencies>
<!-- spring-boot-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- spring-cloud-starter-netflix-eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<!-- spring-cloud依赖管理 -->
<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>
</plugin>
</plugins>
</build>
</project>
- 添加目录结构和之前也是一样的
- 接着就是补充 application.yml 配置文件
- 最后就是写上启动类 EurekaServerApplication.java
新项目的启动类命名与之前一样或者不一样都可以
注意:启动类外层必须加上一个包名,否则会报错无法运行,这也是 Spring Cloud 默认规则
这部分代码和之前依然没什么区别,可以直接复制粘贴
package cn.hmck.eurekaserveranother;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
修改项目 eureka-server 的全局配置文件 application.yml。
注意哦!这里是指修改一开始的 eureka-server 项目
eureka-server 的 application.yml 配置文件内容如下:
节点1配置(server1:7000)
server:
port: 7000
spring:
application:
name: eurake-server
eureka:
client:
fetch-registry: false
register-with-eureka: false
service-url:
defaultZone: http://server2:7009/eureka/ # 指向节点2
instance:
hostname: server1 # 必须与Hosts配置一致
关键配置解析:
fetch-registry: false
:Server节点不获取注册表(仅Client需要)register-with-eureka: false
:Server节点不自我注册- 必须保证:集群节点使用相同的
spring.application.name
简单解释一下
3. 服务提供者集群适配
目标:实现服务多实例注册与负载均衡
按照搭建 eureka-provider 的方式,搭建一个名为 eureka-provider-another 的服务提供者。
eureka-provider-another 的 application.yml 配置文件内容如下:
server:
port: 7007
spring:
application:
name: eureka-provider
eureka:
client:
service-url:
defaultZone: http://localhost:7000/eureka/
instance:
hostname: localhost
为了体现集群,所以我们还需要再搭建一个服务提供者 eureka-provider-another,流程和前面搭建第二个服务器是一样的。并且主要代码和原来的服务提供者 eureka-provider 是一样的。
- 创建项目 eureka-provider-another
- 补充目录结构、配置文件及启动类
- 创建项目 eureka-provider-another
- 补充目录结构、配置文件及启动类
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 http://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>2.1.7.RELEASE</version>
<relativePath/>
</parent>
<!-- 基本信息 -->
<groupId>cn.hmck</groupId>
<artifactId>eureka-provider-another</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 属性 -->
<properties>
<!-- jdk版本 -->
<java.version>1.8</java.version>
<!-- spring-cloud版本 -->
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 依赖 -->
<dependencies>
<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-test</artifactId>
<version>2.1.7.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
</dependencies>
<!-- spring-cloud依赖管理 -->
<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>
</plugin>
</plugins>
</build>
</project>
启动类 EurekaProviderApplication.java 代码
package cn.hmck.eurekaprovider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class EurekaProviderApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaProviderApplication.class, args);
}
}
最终结果展示
4. 改造服务消费者
核心能力:自动故障转移与请求重试
修改项目 eureka-consumer 中的全局配置文件 application.yml。
作为消费者的 eureka-consumer ,我们并没有添加其他的,也没有对其进行修改,代码未作变动。
eureka-consumer 的 application.yml 配置文件内容如下:
server:
port: 7002
spring:
application:
name: eureka-consumer
eureka:
client:
service-url:
defaultZone: http://localhost:7000/eureka
instance:
hostname: localhost
5. 启动测试
依次启动两个 Eureka Server、两个服务提供者、一个服务消费者。启动成功后,无论访问哪个 Eureka Server,Eureka Server 的注册实例都是一样的,访问 server1:7000 的页面效果如下图所示。
访问server2:7009的页面效果如下图所示。
Eureka 的常用配置
1. 心跳机制
Eureka 的心跳机制用于客户端(服务提供者)与 Eureka Server 之间的健康状态维护。通过心跳,客户端定期向 Server 发送信号以表明自身存活状态,避免被 Server 自动剔除。
核心配置参数
在服务提供者的 application.yml
中配置以下参数:
eureka:
instance:
# 心跳间隔时间(默认30秒)
lease-renewal-interval-in-seconds: 30
# 心跳超时时间(默认90秒)
lease-expiration-duration-in-seconds: 90
- lease-renewal-interval-in-seconds:客户端每隔多少秒向 Eureka Server 发送一次心跳。
- lease-expiration-duration-in-seconds:如果 Server 在此时间内未收到心跳,则认为实例不可用并剔除注册信息。
工作原理
- 客户端启动后向 Server 注册,并开始周期性发送心跳。
- Server 接收到心跳后会重置该实例的租约时间。
- 若 Server 在
lease-expiration-duration-in-seconds
内未收到心跳,则标记实例为DOWN
并从注册表中移除。
2. 自我保护机制
Eureka Server 的自我保护机制旨在应对网络分区故障场景。当短时间内丢失大量客户端心跳时,Server 会进入保护模式,保留现有注册信息,避免因网络抖动导致服务被误删。
核心配置参数
在 Eureka Server 的 application.yml
中配置以下参数:
eureka:
server:
# 启用自我保护机制(默认true)
enable-self-preservation: true
# 触发保护模式的阈值(默认0.85,即85%心跳丢失)
renewal-percent-threshold: 0.85
- enable-self-preservation:是否启用自我保护机制,生产环境建议保持
true
。 - renewal-percent-threshold:触发保护模式的心跳丢失比例阈值(例如 0.85 表示 85% 的心跳未更新时触发)。
工作机制
- Server 每分钟统计心跳续约失败的比例。
- 若失败比例超过
renewal-percent-threshold
,则进入保护模式:- 不再剔除任何实例(即使心跳超时)。
- 在 Eureka 控制台显示警告信息:
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP...
- 当心跳恢复正常后,Server 会自动退出保护模式。
配置建议
- 开发环境:可关闭自我保护机制(
enable-self-preservation: false
),方便快速发现失效实例。 - 生产环境:务必启用自我保护机制,避免因网络波动导致服务列表频繁变更。
- 调试技巧:若发现实例未被剔除,需检查是否因保护模式触发导致,可通过日志或控制台提示确认。
特别说明
本次技术实践文档的整理与验证,部分配置优化方案通过 DeepSeek 的智能辅助工具实现效能提升,在此对技术伙伴的支持表示感谢。
技术无界,协作共生
点击访问:DeepSeek 智能开发平台
📣 我是鸿·蒙,若有不解之处或发现疏漏,欢迎随时私信交流!
(虽然不一定秒回,但每条消息都会认真看嗷~ (๑•̀ㅂ•́)و✧)