Introduction
默认的Spring Eureka服务器,服务提供者和服务调用者配置不够灵敏,总是服务提供者在停掉很久之后,服务调用者很长时间并没有感知到变化。或者是服务已经注册上去了,但是服务调用方很长时间还是调用不到(发现不了这个服务)。
在实际情况中,Eureka 的默认设置是不符合我们生产要求的,一般情况下,我们的生产要求:
- ServiceA 下线一台实例后,API-Gateway 的调用不能失败;
- ServiceB 下线一台实例后,ServiceA 的 Feign 调用不能失败;
- 服务上线下线,Eureka服务能够快速感知;
涉及原理知识
Eureka的两层缓存问题
EurekaServer 默认有两个缓存,一个是 ReadWriteMap,另一个是 ReadOnlyMap。有服务提供者注册服务或者维持心跳时时,会修改 ReadWriteMap。当有服务调用者查询服务实例列表时,默认会从 ReadOnlyMap 读取(这个在原生Eureka可以配置,SpringCloud Eureka中不能配置,一定会启用ReadOnlyMap读取),这样可以减少 ReadWriteMap 读写锁的争用,增大吞吐量。EurekaServer 定时把数据从 ReadWriteMap 更新到 ReadOnlyMap 中。
心跳时间
客户端注册服务后,会定时心跳。这个根据客户端的 Eureka 配置中的服务刷新时间决定。还有个配置是客户端的服务过期时间,但是 EurekaServer 默认情况下是忽略客户端的这个字段。需要配置好 EurekaServer 的扫描失效时间,才会触发 EurekaServer 的主动失效机制。
在这个机制启用下:每个客户端会发送自己的服务过期时间上去,EurekaServer 会定时检查每个客户端的服务过期时间和上次心跳时间,如果在服务过期时间内没有收到过任何一次心跳,同时没有处于保护模式下,则会将这个实例从ReadWriteMap中去掉。
调用者服务从Eureka拉列表的轮训间隔
1 | eureka.client.registry-fetch-interval-seconds |
表示 Eureka Server 间隔多久去拉取服务注册信息,默认为30秒,对于api-gateway,如果要迅速获取服务注册状态,可以缩小该值,比如5秒。
1 | eureka.instance.lease-expiration-duration-in-seconds |
表示 Eureka Server 至上一次收到 Client 的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该 Instance。
默认为90秒,如果该值太大,则很可能将流量转发过去的时候,该instance已经不存活了;如果该值设置太小了,则instance则很可能因为临时的网络抖动而被摘除掉。因此,该值至少应该大于lease-renewal-interval-in-seconds。
eureka.instance.lease-renewal-interval-in-seconds:表示eureka client发送心跳给server端的频率。
lease-expiration-duration-in-seconds后,server端没有收到client的心跳,则将摘除该instance。除此之外,如果该instance实现了HealthCheckCallback,并决定让自己unavailable的话,则该instance也不会接收到流量。
Zuul - Ribbon缓存
在 Zuul 内部进行 Routing 和 Load Balance 时候,为了保证 HA,不受 Eureka 掉线的影响,内存中会有一个 Server List 缓存。
Practice
Client
- 服务过期时间,默认90s
1 | eureka.instance.lease-expiration-duration-in-seconds=9 |
超过这个时间没有接收到心跳 EurekaServer 就会将这个实例剔除。EurekaServer 一定要设置 eureka.server.eviction-interval-timer-in-ms 否则这个配置无效,这个配置一般为服务刷新时间配置的三倍。
- 服务刷新时间配置,每隔这个时间会主动心跳一次,默认30s
1 | eureka.instance.lease-renewal-interval-in-seconds=3 |
- 拉服务列表时间间隔,默认30s
1 | eureka.client.registryFetchIntervalSeconds=5 |
- ribbon刷新时间,默认30s
1 | ribbon.ServerListRefreshInterval=5000 |
Server
- 禁用Eureka的ReadOnlyMap缓存
1 | eureka.server.use-read-only-response-cache: false |
- 启用主动失效,并且每次主动失效检测间隔为3s
1 | eureka.server.eviction-interval-timer-in-ms: 3000 |
Zuul - Retry
1 | <dependency> |
1 | #(是否所有操作都重试,若false则仅get请求重试) |
Spring Cloud Gateway
Client 主动通知 Eureka 下线
如果是 Spring Boot 应用,可以调用以下代码通知 Eureka,本服务实例暂停提供服务。
1 | DiscoveryManager.getInstance().shutdownComponent(); |
另外还可以通过 HTTP API 接口删除实例注册信息来实现服务下线。
Reference
- [SpringCloud服务的平滑上下线] https://juejin.im/post/5cf63899f265da1b9253c7f4
- [spring eureka 服务实例实现快速下线快速感知快速刷新配置解析] https://blog.csdn.net/zhxdick/article/details/78560993
- [eureka缓存细节以及生产环境的最佳配置] bhsc881114.github.io/2018/04/01/eureka缓存细节以及生产环境的最佳配置/