在构建微服务体系时,服务治理是协调各服务实例、保障系统稳定运行的关键一环。Spring Cloud 生态中,服务治理与发现功能常通过 Netflix Eureka 实现。为了让开发者能够更便捷地使用,Pivotal 团队将其封装为 Spring Cloud Netflix Eureka 项目。下面我们通过一个具体的项目实例,来拆解服务治理与服务发现的实现过程。
首先,我们需要创建一个包含多个模块的 Spring Boot 项目来模拟微服务环境。项目结构包含 eureka-server(治理中心)、customer 和 goods(业务服务)等模块。



搭建服务治理中心 - Eureka Server
治理中心是整个微服务架构的“大脑”。我们首先在 eureka-server 模块中引入必要的依赖。由于 Eureka Server 本身是一个 Web 应用,所以也需要引入 Web 启动器。
<!-- 引入Spring Boot的Webstarter依赖,它为构建Web应用程序提供了必需的组件,包括Servlet容器和Spring Web MVC。 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入Spring Cloud的Eureka Server starter依赖,用于实现服务注册与发现的功能。 -->
<!-- 它基于Netflix Eureka,提供了微服务架构中服务之间互相发现和通信的能力。 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
接下来,修改 eureka-server 模块的启动类,使用 @EnableEurekaServer 注解来启用 Eureka 服务端功能。
package com.scd.eurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* Eureka Server应用程序入口类。
* 使用@SpringBootApplication注解标记这是一个Spring Boot应用程序,
* 同时通过@EnableEurekaServer注解启用Eureka Server功能,使得当前应用程序成为一个Eureka注册中心。
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
/**
* 程序入口方法。
* 使用SpringApplication.run方法启动Spring Boot应用程序,
* 参数为当前类和应用程序启动参数。
*
* @param args 应用程序启动参数
*/
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}

然后,在 eureka-server 模块的配置文件(如 application.yml)中添加核心配置:
# 定义Spring应用名称,它是一个服务的名称,一个服务可拥有多个实例
spring:
application:
name: eureka-server
# 启动端口
server:
port: 1001
eureka:
client:
# 服务自身就是治理中心,所以这里设置为false,取消注册
register-with-eureka: false
# 取消服务获取,至于服务获取,后续会进行讨论
fetch-registry: false
instance:
# 服务治理中心服务器IP
hostname: 192.168.3.115
配置完成后,启动 eureka-server 模块。

通过浏览器访问 http://localhost:1001/,即可看到 Eureka Server 的管理界面,此时还没有任何服务注册上来。

实现服务发现
Eureka 服务治理中心本身不会主动去“扫描”网络中的服务。服务发现的过程是由各个服务实例(客户端)主动向治理中心发送 REST 请求来完成的,主要包括注册、续约和下线等操作。接下来,我们将 customer 和 goods 模块注册到 Eureka。
首先,在这两个业务服务模块中引入 Eureka 客户端依赖。
<!-- 引入Eureka客户端依赖,用于实现服务发现功能 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
你可以将 Eureka 治理中心视为服务端,而引入 eureka-client 依赖的模块就是客户端。客户端启动后,会自动通过 REST 请求与配置的服务端地址建立联系。
接着,分别修改这两个模块的配置文件。这里以 customer 模块为例,goods 模块配置类似。
customer 服务配置 (application.yml):
# Spring应用名称(服务名称)
spring:
application:
name: customer
# 请求URL,指向Eureka服务治理中心
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1001/eureka/
instance:
# 服务实例主机
hostname: 192.168.3.115
# 服务端口
server:
port: 3001
goods 服务配置 (application.yml):
# Spring应用名称(服务名称)
spring:
application:
name: goods
# 请求URL,指向Eureka服务治理中心
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1001/eureka/
instance:
# 服务实例主机
hostname: 192.168.3.115
# 服务端口
server:
port: 2001
在旧版本的 Spring Cloud 中,可能还需要在启动类上添加 @EnableEurekaClient 注解。但在新版本中,只要引入了 spring-cloud-starter-netflix-eureka-client 依赖,就会自动启用客户端功能,无需修改任何代码。
依次启动 goods 和 customer 模块,等待大约 30 秒后,再次访问 http://localhost:1001/,可以看到两个服务已经成功注册。

页面上出现的红色警告是 Eureka Server 的自我保护机制。当它监测到短时间内有过多服务实例下线时,会触发此机制以防止因网络波动误删健康实例。如果是在开发环境想关闭它,可以在 Eureka Server 的配置中设置:
# 定义Spring应用名称,它是一个服务的名称,一个服务可拥有多个实例
spring:
application:
name: eureka-server
# 启动端口
server:
port: 1001
eureka:
# 服务器配置段,用于定义服务器的行为和特性
server:
# 是否启用自我保护模式
# 自我保护模式是一种机制,用于在服务器负载过高时自动限制某些操作,以保护服务器免于崩溃
enable-self-preservation: false
client:
# 服务自身就是治理中心,所以这里设置为false,取消注册
register-with-eureka: false
# 取消服务获取,至于服务获取,后续会进行讨论
fetch-registry: false
instance:
# 服务治理中心服务器IP
hostname: 192.168.3.115
构建高可用架构
在实际生产环境中,单一的服务实例或单一的服务治理中心都存在单点故障风险。因此,我们需要构建高可用架构:一个服务可以有多个实例,服务治理中心本身也可以有多个节点。
首先,我们利用 IDEA 的“运行配置”功能,为同一个服务启动多个不同端口的实例。以 eureka-server 为例,我们可以在配置中覆盖 server.port 参数。

复制一份运行配置,并将端口改为 1002,这样我们就有了两个 Eureka Server 实例的启动配置。


用同样的方法,为 customer 和 goods 服务也各创建两个运行实例,端口分别为 3001/3002 和 2001/2002。




这样配置后,server.port 会作为命令行参数传入,覆盖 application.yml 文件中的端口设置。但这还不够,我们需要修改服务治理中心和各服务的配置,让它们知道彼此的存在,并能够相互注册。
修改 eureka-server 的配置文件,关键点在于 eureka.client.serviceUrl.defaultZone 需要指向另一个 Eureka Server 节点,让它们互相注册。
# 定义Spring应用名称,它是一个服务的名称,一个服务可拥有多个实例
spring:
application:
name: eureka-server
# 启动端口
# server:
# port: 1001
eureka:
# 服务器配置段,用于定义服务器的行为和特性
server:
# 是否启用自我保护模式
# 自我保护模式是一种机制,用于在服务器负载过高时自动限制某些操作,以保护服务器免于崩溃
enable-self-preservation: false
client:
# 服务自身就是治理中心,所以这里设置为false,取消注册
register-with-eureka: false
# 取消服务获取,至于服务获取,后续会进行讨论
fetch-registry: false
serviceUrl:
# Eureka服务端相互注册
defaultZone: http://localhost:1001/eureka/,http://localhost:1002/eureka/
instance:
# 服务治理中心服务器IP
hostname: 192.168.3.115
同时,修改 customer 和 goods 服务的配置,让它们的 defaultZone 同时指向这两个 Eureka Server,实现客户端的服务发现负载均衡与容错。
goods 服务配置更新:
# Spring应用名称(服务名称)
spring:
application:
name: goods
# 请求URL,指向Eureka服务治理中心
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1001/eureka/,http://localhost:1002/eureka/
instance:
# 服务实例主机
hostname: 192.168.3.115
# 服务端口
server:
port: 2001
customer 服务配置更新:
# Spring应用名称(服务名称)
spring:
application:
name: customer
# 请求URL,指向Eureka服务治理中心
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1001/eureka/,http://localhost:1002/eureka/
instance:
# 服务实例主机
hostname: 192.168.3.115
# 服务端口
server:
port: 3001
重新启动所有模块后,查看控制台日志,确认服务注册成功。

此时分别访问 http://localhost:1001/ 和 http://localhost:1002/,可以看到两个 Eureka Server 页面上都显示了相同的、完整的服务实例列表。这意味着即使其中一个治理中心节点宕机,另一个节点依然能提供服务注册与发现的能力,从而实现了高可用。

这样就初步实现了服务的高可用。注册到治理中心的服务实例,如果被治理中心判断为已挂掉,会被自动剔除。但前提是治理中心本身不能挂掉。为了让多个 Eureka Server 节点之间也能互相感知状态,需要将它们的 register-with-eureka 设置为 true(在上面的配置中我们为了简化先设为 false,实际高可用集群需要设为 true 并相互注册)。重启后,它们会互相注册为服务实例,形成一个对等的集群,任何一个节点宕机都不会影响整体功能。

Eureka 工作原理剖析
整个 Eureka 的运作机制可以概括为下图所示的流程:

Eureka 的服务端与客户端之间,是由客户端主动发送 REST 请求来完成交互的。请求的地址正是通过配置项 eureka.client.serviceUrl.defaultZone 生成的。多个 Eureka Server 节点之间是对等(Peer-to-Peer)关系,数据通过异步复制保持最终一致性。
- 服务注册:一个服务实例想要被治理中心发现,必须先进行注册。客户端将自身信息(如主机、端口、健康状态等)通过 REST 请求发送给治理中心。这里的关键配置是
spring.application.name,Eureka 用它来区分服务类型,相同名称的实例被认为是同一服务的不同副本。注册并非在服务启动后立即发生,默认有 40 秒的延迟,可通过 eureka.client.initial-instance-info-replication-interval-seconds 调整。
- 服务续约:服务实例启动后,可能因故障或下线而变得不可用。为了监控实例健康状态,Eureka 要求客户端实例定期(默认每30秒)发送心跳请求进行续约,以告知治理中心自己仍然存活。如果治理中心在一段时间内(默认90秒)未收到某个实例的续约,则会认为该实例已失效并将其从注册表中剔除。这两个时间可以通过
eureka.instance.lease-renewal-interval-in-seconds 和 eureka.instance.lease-expiration-duration-in-seconds 配置。
- 服务下线:当一个服务实例需要正常关闭时,它会主动向 Eureka Server 发送一个下线请求。治理中心收到请求后,会立即将该实例从注册表中移除,这是一个优雅的注销过程。
通过以上步骤,Eureka 实现了微服务架构中动态的服务治理与发现。虽然现在有更多云原生的选择,但理解 Eureka 的基本原理对于掌握Spring Boot微服务架构的基石仍然至关重要。在实践中,根据业务规模选择合适的服务发现组件,并合理配置参数,是构建稳定可靠的分布式系统的关键。如果你想探讨更多关于微服务架构或云原生技术的话题,欢迎在云栈社区交流分享。