Eureka节点信息同步延迟及单个注册中心节点load偏高问题的排查

一、问题现象

1、网关会报访问后端正在发布的应用超时的错误:

 

2、Eureka注册中心负载Load不均衡

 

二、问题分析

1、Eureka客户端同步节点信息的分析

Eureka-Client中的com.netflix.discovery.DiscoveryClient类,为负责与Eureka注册中心进行沟通协调的实现类,包括应用节点往注册中心注册、应用节点状态向注册中心更新状态、从注册中心同步注册应用信息等功能。

com.netflix.discovery.DiscoveryClient.initScheduledTasks()方法初使化定期从注册中心拉取应用节点信息、定期向注册中心发送心跳等的线程,从注册中心拉取应用节点信息的线程初使化实现代码如下:

 

控制同步频率及延迟的两个重要的参数为registryFetchIntervalSeconds及expBackOffBound,下面分别介绍:

  • registryFetchIntervalSeconds:该配置对应于配置项eureka.client.registry-fetch-interval-seconds,表示客户端从Eureka注册中心同步节点信息的时间间隔,默认值为30,为了及时的发现应用节点的变化,生产环境当前的配置为5,单位为秒。

注意事项:

1)该值是正常从注册中心同步的间隔时间,但是如果同步的时候发生超时,则下 次同步的延迟时间会翻倍,如10秒、20秒、40秒......但是最大的同步延迟时间不 会超过registryFetchIntervalSeconds与expBackOffBound的乘积,代码如下:

int maxDelay = registryFetchIntervalSeconds * expBackOffBound;

int newDelay = Math.min(maxDelay, currentDelay * 2);
  • 当从配置中心同步成功后,又会将下次同步的延迟时间设置为 registryFetchIntervalSeconds的值,如这里的5秒。

代码逻辑如下:

 

  • expBackOffBound:该配置对应于配置项eureka.client.cache-refresh-executor-exponential-back-off-bound,表示客户端从注册中心同步节点信息失败后,下次再次同步配置的时间间隔的最大倍数,默认值为10,即从Eureka注册中心同步信息的延迟时间最长可能为5*10=50秒,这就有可能造成从Eureka注册中心同步信息延迟增大的风险,该值现在生产将其设置为1,即确保每次的同步时间都为固定值registryFetchIntervalSeconds。

 

注:

客户端从注册中心拉取配置的实现方法为com.netflix.discovery.DiscoveryClient.fetchRegistry(boolean),如果有必要可以通过反射手动触发该方法从注册中心更新配置。

2、Eureka客户端配置多个注册中心节点的配置获取

Eureka注册中心通常需要确保高可用,因此往往是以集群的方式布署,在Eureka客户中可以通过配置项eureka.client.service-url指定一个或者多个注册中心的地址,配置项如下所示:

 

注:service-url是map结构,defaultZone为其一个key。

当客户端配置了多个注册中心地址时,客户端使用注册中心的策略为“能用就一直用”的策略,当某个注册中心地址不可用时再换其它的,因而在不同的应用中配置多个注册中心时,建议将其顺序做一下不同的交换,以确保注册中心使用的均衡性,也可以将多个注册中心放在负载均衡的后面,由负载均衡通过负载均衡策略来做相应的调度。

客户端使用注册中心节点的方法为com.netflix.discovery.shared.transport.decorator.RetryableEurekaHttpClient.execute(RequestExecutor<R>),代码实现如下:

 

 

3、Eureka注册中心缓存配置的管理

服务注册到Eureka注册中心后,服务实例信息是存储在注册表中的,也就是内存中。但Eureka为了提高响应速度,在内部做了优化,加入了两层的缓存结构,将Client需要的实例信息直接缓存起来,获取的时候直接从缓存中拿数据然后响应给Client。 第⼀层缓存是readOnlyCacheMap,readOnlyCacheMap是采用ConcurrentHashMap来存储数据的,主要负责定时与readWriteCacheMap进⾏数据同步,默认同步时间为 30 秒⼀次。

第⼆层缓存是readWriteCacheMap,readWriteCacheMap采⽤Guava来实现缓存。缓存过期时间默认为180秒,当服务下线、过期、注册、状态变更等操作都会清除此缓存中的数据。

注册中心负责管理应用节点缓存的实现类为Eureke-Core包中的com.netflix.eureka.registry.ResponseCacheImpl,存储缓存的Map代码块如下:

 

一级缓存从二级缓存中同步数据的代码为com.netflix.eureka.registry.ResponseCacheImpl.getCacheUpdateTask()方法,代码块如下:

 

其中的参数shouldUseReadOnlyResponseCache控制是否需要开启一级缓存,其对应的配置项为eureka.server.use-read-only-response-cache,默认值为true。

客户端获取服务实例数据时,会先从⼀级缓存中获取,如果⼀级缓存中不存在,再从二级缓存中获取,然后再返回给客户端,读取应用信息的ResponseCacheImpl.getValue实现代码如下所示:

 

Eureka 之所以设计二级缓存机制,也是为了提高Eureka Server 的响应速度,缺点是缓存会导致 Client获取不到最新的服务实例信息,然后导致无法快速发现新的服务和已下线的服务。

了解了服务端的实现后,想要解决这个问题就变得很简单了,我们可以缩短只读缓存的更新时间(eureka.server.response-cache-update-interval-ms)让服务发现变得更加及时,或者直接将只读缓存关闭(eureka.server.use-read-only-response-cache=false),多级缓存也导致客户与注册中心数据⼀致性很薄弱。

Eureka注册中心会有定时任务去检测失效的服务,将服务实例信息从注册表中移除,也可以将这个失效检测的时间缩短,这样服务下线后就能够及时从注册表中清除。

 

三、问题解决

1、客户端配置

客户端增加或修改配置同步延迟的配置如下:

eureka:   

  client:   

    registry-fetch-interval-seconds: 5                     

    cache-refresh-executor-exponential-back-off-bound: 1

表示每隔5秒定期从Eureka注册中心同步一次配置,每次间隔的最长时间为5*1=5秒。

2、注册中心配置

Eureka注册中心增加禁止使用一级缓存的配置:

eureka.server.use-read-only-response-cache: false

3、客户端配置注册中心考虑均衡策略

1)在不同应用的客户端配置service-url时,将注册中心的地址进行交换;

2)将所有的注册中心的地址放到负载均衡(如Nginx)后面,由负载均衡来确保访问的均衡性;

4、运维修改下线应用在可用时间

应用发布后并注册到Eureka注册中心,应用客户端从注册中心获取到新的节点,以下几个因素都可能导致客户端获取到新的节点并替换旧的节点:

  • Eureka注册中心存在着一级缓存到二级缓存的数据同步时间;
  • 客户端从注册中心同步的频率;
  • 客户端从注册中心同步时,注册中心负载可高或者当前存在着网络问题,导致客户端同步失败时,从新同步增加同步的时间;
  • 客户端本地负载均衡获取新的节点的频率;

因为需要运维在发布时,在新的节点发布成功后,将旧的节点保留更长时间的可用性,如1分钟等。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值