Eureka 的自我保护机制
Eureka 的自我保护机制(Self-Preservation Mechanism)是 Netflix Eureka 设计中一个非常核心且经典的特性。它的主要目的是避免在发生网络异常(如网络分区、短暂的网络抖动)时,Eureka Server 盲目地注销(剔除)掉大量实际上仍然健康的微服务实例。
Eureka 的设计哲学是:“宁可保留错误的注册信息,也不盲目注销任何可能健康的微服务”。这也是 Eureka 遵循 CAP 定理中 AP(可用性 + 分区容错性) 原则的具体体现。
以下是对 Eureka 自我保护机制的详细解析:
1. 为什么需要自我保护机制?
正常情况下,微服务客户端(Eureka Client)会定期(默认每 30 秒)向 Eureka Server 发送心跳(Heartbeat),以证明自己还“活着”。如果 Eureka Server 在一定时间内(默认 90 秒)没有收到某个实例的心跳,就会将该实例从注册表中剔除。
但是,考虑以下场景:
- 网络分区/网络抖动: 微服务实例本身运行非常健康,但它与 Eureka Server 之间的网络突然出现了故障。
- Eureka Server 自身负载过高: 导致无法及时处理心跳请求。
如果按照正常逻辑,Eureka Server 会因为没收到心跳而把这些健康的实例全部剔除。这就导致了灾难性的后果:消费者无法获取服务提供者的地址,整个系统雪崩。
为了防止这种情况,Eureka 引入了自我保护机制。
2. 触发条件与工作原理
自我保护机制的触发基于一个数学计算:期望的心跳数与实际收到的心跳数的比例。
- 统计窗口: 默认情况下,Eureka Server 会每 15 分钟统计一次心跳失败的比例。
- 阈值(Threshold): 默认阈值是 85%。
- 期望心跳数: 如果一个 Eureka Server 有
N个注册实例,每个实例每 30 秒发一次心跳(即每分钟 2 次)。那么一分钟的期望心跳总数是2 * N。
触发条件:
如果在 15 分钟内,Eureka Server 收到的实际心跳数量 低于 期望心跳数量的 85%(即大量实例突然“失联”),Eureka 就会认为这不是微服务挂了,而是网络出了问题,从而触发自我保护机制。
3. 触发自我保护机制后的表现
一旦进入自我保护模式,Eureka Server 会采取以下行动:
- 停止剔除: Eureka Server 将不再从注册表中剔除任何因为长时间没收到心跳而过期的服务实例。
- 继续接受请求: 它仍然能够接受新微服务的注册和查询请求,并且依然会和其他 Eureka Server 节点进行数据同步(保证高可用)。
- UI 警告: Eureka 的 Web 控制台页面会出现一行醒目的红色警告文字:
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
(紧急情况!Eureka可能正在错误地声称实例处于运行状态。由于续约数量低于阈值,为了安全起见,实例不会被过期剔除。)
当网络恢复,Eureka Server 收到的心跳数量重新超过 85% 的阈值时,它会自动退出自我保护模式,恢复正常的剔除逻辑。
4. 优缺点分析
- 优点(最大化可用性): 在网络极度不稳定的情况下,依然能提供服务发现功能。即便返回给消费者的实例列表中包含了几个真正挂掉的实例,也比因为网络问题把所有实例清空(导致消费者完全无法调用)要好。
- 缺点(牺牲一致性): 客户端可能会拿到那些实际上已经宕机(但被 Eureka 保护起来没剔除)的实例地址。
- 解决方案: 这通常需要结合客户端的负载均衡器(如 Spring Cloud LoadBalancer / Ribbon) 和 容错机制(如 Resilience4j / Hystrix 的重试、熔断) 来解决。如果调用某个实例失败,客户端会自动重试其他实例。
5. 相关配置与调优
在不同的环境中,我们对自我保护机制的需求是不同的:
A. 生产环境 (Production)
强烈建议保持开启。
这是保障系统高可用的重要防线。默认就是开启的。
B. 开发 / 测试环境 (Dev / Test)
建议关闭。
在开发和测试时,我们经常频繁地重启服务。如果开启了自我保护,Eureka 会一直保留着我们关掉的旧实例,导致其他开发者的请求被打到已经不存在的实例上,报 Connection Refused 错误。
如何关闭和调整参数?
Eureka Server 端配置:
yamleureka: server: # 开启或关闭自我保护机制 (生产选true,开发选false) enable-self-preservation: false # 清理无效节点的时间间隔,默认60000毫秒(60秒) eviction-interval-timer-in-ms: 4000 # 触发自我保护的阈值,默认是 0.85 (85%) renewal-percent-threshold: 0.85Eureka Client (微服务实例) 端配置:
yamleureka: instance: # 客户端向服务端发送心跳的间隔时间,默认30秒 lease-renewal-interval-in-seconds: 30 # 服务端在收到最后一次心跳后等待的时间上限,默认90秒。超时则剔除。 lease-expiration-duration-in-seconds: 90
总结
Eureka 的自我保护机制是应对分布式网络环境不可靠性的一种有效手段。它遵循了“宁纵勿枉”的原则,优先保证系统的可用性(AP)。理解并合理配置它,对于构建健壮的微服务架构至关重要。