Netflix Eureka:微服务如何找到彼此?
Netflix Eureka:微服务如何找到彼此?
摘要: 微服务实例会动态扩缩容、重启和迁移,靠写死 IP 地址很快就会把系统变成一张过期的通讯录。Netflix Eureka 提供了一套服务注册与发现机制:实例登记自己的位置,客户端查询注册表,再结合本地缓存与客户端负载均衡完成服务调用。
关键词: Netflix Eureka、服务注册、服务发现、Spring Cloud、微服务
一、从“写死地址”说起
假设订单服务要调用库存服务。最直接的写法,是把库存服务地址写进配置:http://10.0.2.17:8080。
开发环境里这招挺省事,规模一大就开始闹脾气:库存服务一扩容,调用方不知道新实例;实例换机器或端口,配置得跟着改;某台机器挂了,旧地址还留在通讯录里。服务之间原本只是互相调用,最后却变成了“全公司一起维护 IP 地址表”。
服务注册与发现就是来解决这件事的。服务实例启动后向注册中心登记自己的身份和访问地址;调用方按服务名查询可用实例,再把请求发给其中一个。Netflix Eureka 是 Netflix 开源的 RESTful 服务注册与发现组件,主要面向中间层服务的发现、负载均衡与故障转移。citeturn175258search0
二、Eureka 的核心角色
Eureka 通常由两类角色组成:
- Eureka Server: 维护服务实例注册信息,并向客户端提供注册表查询接口。多个 Server 可以互相作为 peer 同步注册信息。
- Eureka Client: 既可以是向 Server 登记自身信息的服务实例,也可以是查询其他服务地址的调用方。一个应用往往同时承担这两种角色。
注册信息通常包含应用名、实例 ID、主机名、端口、状态和元数据等。Spring Cloud Netflix 会把 Spring Boot 应用接入 Eureka;引入 Eureka Client starter 后,应用可以自动注册,同时作为客户端发现其他服务。citeturn714340view3turn714340view3
可以把它想成一个会定期更新的通讯录:服务实例负责报到,Eureka 负责维护目录,调用方根据服务名查到当前可用的地址。
三、一次注册与调用是怎么完成的
典型流程大致如下:
- 实例启动并注册。 服务启动后,Eureka Client 将应用名、实例地址、端口及元数据发送给 Eureka Server。
- 持续发送心跳。 客户端周期性续约,表明实例仍在运行。若实例正常关闭,客户端会尝试注销;若异常失联,Server 会在后续过期清理中处理。
- 客户端拉取注册表。 调用方从 Eureka 获取可用实例列表,并在本地缓存注册信息。
- 按服务名选择实例。 应用使用 Spring Cloud LoadBalancer 等客户端侧组件,从列表中选择目标实例,再发起 HTTP/RPC 请求。
- 状态变化传播。 注册、续约、注销等变更会在 Eureka 集群节点间复制;客户端通过后续注册表更新看到变化。
一个关键点:Eureka 负责服务目录,不负责替你的业务请求做完整的负载均衡。 它给客户端提供实例信息;具体选择哪个实例,通常由调用端的负载均衡组件完成。Spring Cloud Netflix 也提供 Eureka 与 Spring Cloud LoadBalancer 的集成。citeturn714340view3
四、为什么 Eureka Client 也要缓存注册表
如果每次业务调用前都同步询问注册中心,Eureka Server 就会落在每条业务链路的热路径上:注册中心一抖,服务调用也跟着抖。Eureka 客户端维护本地注册表缓存,服务发现通常先从本地信息中完成,降低了对中心节点的即时依赖。Spring Cloud 文档也明确说明,服务端和客户端都持有内存中的注册信息缓存。citeturn714340view3
这是一种典型的可用性取舍:注册信息不是每一毫秒都绝对新鲜,但调用方在短暂的注册中心故障期间,仍可能依据已有缓存继续发现服务。相应地,网络分区或实例故障时,缓存里也可能暂时保留不可达实例,因此调用端仍要做好超时、重试边界和故障处理。
五、Peer 复制与高可用
生产环境通常不会只放一个 Eureka Server。多个 Server 可以互相配置为 peer,并复制服务注册信息。Spring Cloud 文档指出 Eureka Server 没有后端持久化存储,注册信息主要保存在内存中,客户端也维护本地缓存;因此,高可用需要通过多个 peer、合理的部署拓扑和实例自身的心跳共同实现。citeturn714340view3turn714340view1
这里有个容易踩的认知坑:Peer 复制不等于强一致数据库,也不是共识协议。 网络分区期间,不同节点可能短暂持有不同视图;网络恢复后,节点再通过复制逐步收敛。调用端应该能容忍短时间的实例列表差异,而不是假设所有节点在任意时刻都看到完全相同的注册表。citeturn714340view1
六、自我保护:宁可保留旧记录,也别误删一大片
Eureka 的自我保护机制是它最常被讨论、也最容易被误解的设计之一。
如果大量客户端同时无法续约,原因可能是实例真的集体宕机,也可能只是网络故障、机房隔离或注册中心链路出了问题。此时若 Server 把所有未续约实例立刻清掉,网络恢复后客户端可能拿到一份被误删的空注册表。自我保护的思路是:当续约数量低于预期阈值时,暂停或减少过期实例清理,尽量保留已有注册信息,避免一次网络波动演变成全局服务发现故障。citeturn714340view1turn714340view1
它的代价也很明确:如果实例确实已经宕机,注册表可能会在一段时间内保留过期记录。调用方因此需要快速超时并尝试其他实例;运维侧则要同时看续约率、网络状况和实例健康,而不是只盯着控制台里“保护模式已开启”这一行字。
别把关闭自我保护当作通用修复按钮。 它可能让过期地址清得更快,却也会让网络抖动时的注册表更脆弱。先判断是心跳阈值、实例规模变化、网络分区还是健康检查配置问题,再决定是否调整参数。Eureka 项目文档描述的默认阈值与行为可配置,实际运行值应以部署配置和所用版本为准。citeturn714340view1
七、Spring Cloud Netflix 快速接入
以下示例展示单机开发环境中的最小配置。依赖版本应通过与当前 Spring Boot 版本兼容的 Spring Cloud Release Train 管理,不要把示例里的依赖片段当成可以随手拼版本的许可证。
1. Eureka Server
在 Maven 中添加 Server starter:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
启动类启用 Eureka Server:
@SpringBootApplication
@EnableEurekaServer
public class RegistryApplication {
public static void main(String[] args) {
SpringApplication.run(RegistryApplication.class, args);
}
}
单机开发环境可以配置为独立 Server,不向自己注册,也不从自己拉取注册表:
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:8761/eureka/
defaultZone 是 Map 键名,注意大小写;Spring Cloud 文档特别指出它使用 defaultZone 这一大小写形式。citeturn714340view3
2. 业务服务作为 Eureka Client
添加 Client starter:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
配置应用名、端口和 Server 地址:
spring:
application:
name: inventory-service
server:
port: 8081
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
启动应用后,Client starter 会让应用向 Eureka 注册。多个同名实例可以使用相同的 spring.application.name,但实际部署时仍要保证每个实例 ID 唯一。默认配置会结合主机名、应用名和端口生成实例标识;也可以通过 eureka.instance.instanceId 显式覆盖。citeturn714340view3turn714340view3
调用方通过 Spring Cloud LoadBalancer 等组件按服务名选择实例。应用层应调用逻辑服务名,而不是把某个实例 IP 固定写进代码;同时还要配置合理的连接、读取超时和调用失败策略。
八、生产环境里值得提前想清楚的事
多节点与网络拓扑
- 至少规划多个 Eureka Server,并验证 peer 之间可互通、配置一致。
- 根据机房或可用区布置节点,尽量让客户端优先使用本地或同区域节点。
- 明确防火墙、安全组、DNS 和负载均衡的访问路径,避免“端口通了,但 peer 地址不可达”。
实例地址与身份
- 容器或多网卡环境里要确认注册出去的是调用方实际可达的地址,而不是错误的容器内地址或管理网卡地址。
- 多实例部署时保证实例 ID 唯一;重复 ID 会让注册记录互相覆盖或产生难以排查的异常行为。
- 评估优雅停机时的注销过程,并在发布平台上留出摘流与实例退出时间。
健康状态与调用容错
- 心跳说明客户端仍能和 Eureka 通信,不等同于业务接口一定健康。
- 注册表更新存在传播延迟,客户端缓存也意味着实例状态不会在所有调用方同时瞬时更新。
- 调用端仍需设置超时,并根据业务幂等性设计有限重试、熔断或降级;注册中心不能替代这些机制。
可观测性
重点观察注册实例数量、续约成功率、自我保护状态、peer 同步情况、注册表拉取失败,以及业务请求的超时率和实例不可达率。只有把注册中心指标与业务调用指标放在一起看,才能分清“服务没注册上”和“注册了但服务不可用”这两类问题。
九、Eureka 的优势与边界
适合的地方:
- JVM/Spring Cloud 微服务体系,需要成熟的服务注册与发现集成。
- 希望客户端使用本地缓存,降低服务发现链路对中心节点的即时依赖。
- 需要以服务名访问动态变化的实例,并接受短暂的注册信息不一致。
需要权衡的地方:
- 注册数据主要位于内存中,不提供传统数据库式的持久化注册表。
- 网络分区时更偏向保留已有注册信息,可能短暂暴露已经失效的实例。
- 需要自行设计服务调用侧的负载均衡、超时、重试与健康治理。
- 如果平台已经由 Kubernetes Service、云厂商服务发现或其他控制面统一管理服务地址,再额外引入 Eureka 可能形成两套服务目录,增加运维复杂度。
没有哪个注册中心能脱离部署环境成为“闭眼选”的答案。Eureka 的设计重点是可用性与客户端自治;选型时要把一致性预期、故障模型、生态集成和团队运维能力放在一张桌上比较。
十、总结
Netflix Eureka 做的事很朴素,却是微服务体系的基础设施:服务实例动态注册,客户端按服务名发现实例,缓存和 peer 复制提高了故障场景下的可用性,自我保护则避免网络问题触发大规模误清理。
记住三个要点就够打底:
- Eureka 是服务注册与发现组件,不是业务请求的负载均衡器。
- 注册信息、缓存和 peer 复制带来可用性,也意味着故障期间可能存在短暂陈旧或不一致的数据。
- 自我保护不是“服务健康证明”,客户端仍需要超时、故障处理和可观测性。
把这些边界想明白,再接入 Eureka,系统就不至于把“找得到服务”误当成“服务一定可用”。
参考资料
- Netflix Eureka 项目 README:Eureka 的定位与用途。citeturn175258search0
- Netflix Eureka Wiki:自我保护模式、Peer-to-Peer 通信。citeturn175258search1turn714340view1turn714340view1
- Spring Cloud Netflix 官方参考文档:Eureka Client、Server、注册与高可用配置。citeturn714340view3turn714340view3
- 点赞
- 收藏
- 关注作者
评论(0)