所有分类
  • 所有分类
  • 未分类

Spring Cloud Eureka原理–续期/自我保护

​简介

本文介绍SpringCloud的Eureka是如何进行续期和自我保护的。

本内容也是Java后端面试常见的问题。

续期

服务续期和服务注册非常相似,服务注册在Eureka Client程序启动之后开启,并同时开启服务续期的定时任务

在eureka-client-1.6.2.jar的DiscoveryClient类下有renew()方法,其代码如下:

/**
* Renew with the eureka service by making the appropriate REST call
*/
boolean renew() {
	EurekaHttpResponse<InstanceInfo> httpResponse;
	try {
		httpResponse = eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), 
			instanceInfo.getId(), instanceInfo, null);
            
		logger.debug("{} - Heartbeat status: {}", 
            PREFIX + appPathIdentifier,
            httpResponse.getStatusCode());
            
		if (httpResponse.getStatusCode() == 404) {
			REREGISTER_COUNTER.increment();
			logger.info("{} - Re-registering apps/{}", 
                PREFIX + appPathIdentifier,
				instanceInfo.getAppName());
			return register();
		}
        
		return httpResponse.getStatusCode() == 200;
	} catch (Throwable e) {
		logger.error("{} - was unable to send heartbeat!", PREFIX + appPathIdentifier, e);
		return false;
	}
}

另外,EurekaServer的续约接口在eureka-core:1.6.2.jar的com.netflix.eureka包下的InstanceResource类下,接口方法为renewLease(),它是一个RESTfulAPI接口。为了减少本章的篇幅,省略了大部分代码的展示。其中有一个registry.renew()方法,即服务续约,代码如下:

@PUT
public Response renewLease(...参数省略){
	// 代码省略
	boolean isSuccess=registry.renew(app.getName(),id, isFromReplicaNode);
	// 代码省略
}

可以跟踪 registry.renew 的代码继续深入研究,和追踪服务注册的源码类似,在此不再赘述。服务续约有两个参数是可以配置的(官方建议不要修改):

  1. Eureka Client 发送续约心跳的间隔。(默认为30秒)
  2. Eureka Server 在多长时间内没有收到心跳将实例剔除的时间参数。(默认为90秒)

配置方法:分别在 Eureka Client 和 Eureka Server 的配置文件 application.yml 中加以下的配置: 

eureka.instance.leaseRenewalIntervalInSeconds
eureka.instance.leaseExpirationDurationInSeconds

最后,有关服务注册列表的获取、服务下线和服务剔除的源码不在这里进行跟踪解读,因为与服务注册和续约类似,有兴趣的读者可以自行研究。 

自我保护

当有一个新的 Eureka Server 出现时,它尝试从相邻 Peer 节点获取所有服务实例注册表信息。如果从相邻的 Peer 节点获取信息时出现了故障, Eureka Server 会尝试其他的 Peer 节点。如果 Eureka Server 能够成功获取所有的服务实例信息,则根据配置信息设置服务续约的阈值。

在任何时间,如果 Eureka Server 接收到的所有服务续约低于为该值配置的百分比(默认为 15 分钟内低于 85%),则Eureka Server开启自我保护模式,即不再剔除注册列表的信息。

这样做的好处在于,如果是 Eureka Server 自身的网络问题而导致 Eureka Client 无法续约,Eureka Client 的注册列表信息不再被删除,也就是 Eureka Client 还可以被其他服务消费。

默认情况下, Eureka Server 的自我保护模式是开启的,如果需要关闭,则在配置文件添加以下代码:

eureka:
  server:
    enable-self-preservation: false

为什么 Eureka Client 获取服务实例很慢

1. Eureka Client 的注册延迟

Eureka Client 启动之后,不是立即向 Eureka Server 注册的,而是有一个延迟向服务端注册的时间。通过跟踪源码,可以发现默认的延迟时间为 40 秒,源码在 eureka-client-1.6.2.jar 的DefaultEurekaClientConfig 类中,代码如下:

public int getInitialInstanceInfoReplicationIntervalSeconds() {
    return configInstance.getIntProperty(
               namespace + INITIAL_REGISTRATION_REPLICATION_DELAY_KEY, 40
           ).get();
}

2. Eureka Server 的响应缓存

Eureka Server 每 30 秒更新一次响应缓存,可通过更改配置 eureka.server.responseCacheUpdateIntervalMs 来修改。所以即使是刚刚注册的实例,也不会立即出现在服务注册列表中。

3. Eureka Client 的缓存

Eureka Client 保留注册表信息的缓存。 该缓存每 30 秒更新一次(如前所述)。 因此, Eureka Client刷新本地缓存并发现其他新注册的实例可能需要 30 秒。

4. LoadBalancer 的缓存

Ribbon 的负载平衡器从本地的 Eureka Client 获取服务注册列表信息。 Ribbon 本身还维护了缓存,以避免每个请求都需要从 Eureka Client 获取服务注册列表。此缓存每 30 秒刷新一次(可由 ribbon.ServerListRefreshInterval 配置),所以可能至少需要 30 秒的时间才能使用新注册的实例。

综上因素,一个新注册的实例,默认延迟 40 秒向服务注册中心注册,所以不能马上被Eureka Server 发现。另外,刚注册的 Eureka Client 也不能立即被其他服务调用,原因是调用方由于各种缓存没有及时获取到最新的服务注册列表信息。

0

评论0

请先

显示验证码
没有账号?注册  忘记密码?

社交账号快速登录