eureka源码解析——服务故障自动剔除
文章目录
- 前言
- 一、服务故障剔除源码解析
- 1.1、自动剔除定时任务启动
- 1.1.1、AbstractInstanceRegistry#postInit
- 1.2、服务剔除流程
- 1.2.1、EvictionTask#run
- 1.2.2、EvictionTask#evict
前言
服务注册到
eurekaServer上之后,会定时30s发送心跳请求来进行续约,如果服务宕机了或者其他情况导致没有主动向EurekaServer发送服务下线请求,那么EurekaServer在后台会启动一个定时后,默认60s扫描一次注册表,看看哪些服务实例规定时间没有发送续约请求,将这些故障实例从注册表剔除。
一、服务故障剔除源码解析
1.1、自动剔除定时任务启动
之前将
EurekaServer启动的时候,在EurekaBootStrap的initEurekaServerContext方法中会调用registry的openForTraffic方法中,在openForTraffic方法中会调用super.postInit();方法也就是AbstractInstanceRegistry#postInit方法会启动定时任务

1.1.1、AbstractInstanceRegistry#postInit
这个方法中干了两件事:
- 调用renewsLastMin.start启动一个定时任务,用于统计每分钟的服务续约的renew统计
- 启动一个定时任务,用于做服务剔除的,默认60s执行一次
protected void postInit() {renewsLastMin.start();if (evictionTaskRef.get() != null) {evictionTaskRef.get().cancel();}evictionTaskRef.set(new EvictionTask());//60s执行一次evictionTimer.schedule(evictionTaskRef.get(),serverConfig.getEvictionIntervalTimerInMs(),serverConfig.getEvictionIntervalTimerInMs());}
1.2、服务剔除流程
1.2.1、EvictionTask#run
这里会调用
evict方法进行剔除操作
public void run() {try {// 这个是防止服务器时钟发生问题,做的时间补偿long compensationTimeMs = getCompensationTimeMs();logger.info("Running the evict task with compensationTime {}ms", compensationTimeMs);// 驱逐方法evict(compensationTimeMs);} catch (Throwable e) {logger.error("Could not run the evict task", e);}}
1.2.2、EvictionTask#evict
- 这里首先会判断是否触发了自我保护机制,触发机制如下 比如说每一个新的实例注册到
Eureka Server,Eureka都会记录下来当前注册了多少个实例,再就是默认客户端每30s进行服务续约一次,一分钟1个实例就是2次服务续约,它都会记录下来,按照这个规则,如果我有10个实例注册到Eureka Server上,那么按照正常流程下,每分钟就会有20个服务续约请求,当然这是最好的情况下,不好的情况下服务续约请求可能达不到20次,这个时候它就有一个最小阈值,也就是85%,这里就是需要最少有15次服务续约,当低于这个阈值的时候,就会自动开启自我保护机制,这次服务剔除就不会再进行。- 遍历注册表,判断续约有没有到期,如果到期则进行剔除,这里剔除会走我们上文说到的服务下线的逻辑,并且不进行节点间的同步,这个因为可能存在网络分区,可能实例只是和当前节点的心跳断了。
public void evict(long additionalLeaseMs) {logger.debug("Running the evict task");//自我保护机制,是否启用服务故障剔除/*** 自我保护机制触发流程, 比如说每一个新的实例注册到Eureka Server ,Eureka 都会记录下来当前注册了多少个实例,再就是默认客户端每30s进行服务续约一次,* 一分钟1个实例就是2次服务续约,它都会记录下来,按照这个规则,如果我有10个实例注册到了Eureka Server 上,那么按照正常流程下,每分钟就会有20个服务续约请求,当然这是最好的情况下* ,不好的情况下服务续约请求可能达不到20次,这个时候它就有一个最小阈值,也就是85% ,这里就是需要最少有15 次服务续约,当低于这个阈值的时候,就会自动开启自我保护机制,这次服务剔除就不会再进行*/if (!isLeaseExpirationEnabled()) {logger.debug("DS: lease expiration is currently disabled.");return;}// We collect first all expired items, to evict them in random order. For large eviction sets,// if we do not that, we might wipe out whole apps before self preservation kicks in. By randomizing it,// the impact should be evenly distributed across all applications.List<Lease<InstanceInfo>> expiredLeases = new ArrayList<>();// 遍历注册表,遍历每一个实例信息,调用每个实例租约的lease.isExpired()方法判断有没有过期,additionalLeaseMs是上面计算的补偿时间for (Entry<String, Map<String, Lease<InstanceInfo>>> groupEntry : registry.entrySet()) {Map<String, Lease<InstanceInfo>> leaseMap = groupEntry.getValue();if (leaseMap != null) {for (Entry<String, Lease<InstanceInfo>> leaseEntry : leaseMap.entrySet()) {//当前时间 距离服务上次续约时间超过 90+90 s,超过180s认为过期了。Lease<InstanceInfo> lease = leaseEntry.getValue();if (lease.isExpired(additionalLeaseMs) && lease.getHolder() != null) {expiredLeases.add(lease);}}}}// To compensate for GC pauses or drifting local time, we need to use current registry size as a base for// triggering self-preservation. Without that we would wipe out full registry.// 注册表中所有实例信息int registrySize = (int) getLocalRegistrySize();// 85%int registrySizeThreshold = (int) (registrySize * serverConfig.getRenewalPercentThreshold());// 计算剔除范围,就是不超过注册表所有实例的15%int evictionLimit = registrySize - registrySizeThreshold;// 剔除数量不超过注册表实例的15%int toEvict = Math.min(expiredLeases.size(), evictionLimit);if (toEvict > 0) {logger.info("Evicting {} items (expired={}, evictionLimit={})", toEvict, expiredLeases.size(), evictionLimit);Random random = new Random(System.currentTimeMillis());for (int i = 0; i < toEvict; i++) {// Pick a random item (Knuth shuffle algorithm)int next = i + random.nextInt(expiredLeases.size() - i);Collections.swap(expiredLeases, i, next);Lease<InstanceInfo> lease = expiredLeases.get(i);String appName = lease.getHolder().getAppName();String id = lease.getHolder().getId();EXPIRED.increment();logger.warn("DS: Registry: expired lease for {}/{}", appName, id);//服务剔除,这里走的是服务下线逻辑,并且不进行节点间的同步internalCancel(appName, id, false);}}}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
