正文
服务剔除是指:服务端会定时对实例进行检查检查,如果有没有发送心跳续约的实例,则将这个实例从注册表中剔除。所以这个功能是在服务端实现的,调用链路:
-
com.netflix.eureka.EurekaBootStrap#contextInitialized
-
com.netflix.eureka.EurekaBootStrap#initEurekaServerContext
-
com.netflix.eureka.registry.InstanceRegistry#openForTraffic
-
com.netflix.eureka.registry.AbstractInstanceRegistry#postInit
-
com.netflix.eureka.registry.AbstractInstanceRegistry.EvictionTask#run
- com.netflix.eureka.registry.AbstractInstanceRegistry.EvictionTask#getCompensationTimeMs
- com.netflix.eureka.registry.AbstractInstanceRegistry#evict(long)
-
-
-
-
com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#openForTraffic方法:
该方法会计算出server端每分钟需要收到的总心跳数和阈值,阈值默认是心跳数*0.85。
每60s执行定时剔除任务
然后调用com.netflix.eureka.registry.AbstractInstanceRegistry#postInit方法:
该方法会启动一个每60s执行一次的定时剔除实例任务,我们来看这个任务的逻辑:
首先计算出一个“补偿时间”,然后以补偿时间为参数,执行真正的剔除逻辑,我们来看这个计算“补偿时间”的方法:
补偿时间
该方法中会计算本次定时任务执行的时间戳和上次定时任务执行的时间戳,这个时间戳在默认配置的情况下,应该等于60s,因为定时任务是每60s执行一次。如果因为server的内部原因(比如gc)导致差值大于60s的话。那么就会影响剔除服务实例的准确性,所以需要计算出这个“补偿时间”。
![image-20210626112523203](../../../../../Library/Application Support/typora-user-images/image-20210626112523203.png)
具体怎么影响,下文会讲解。
判断是否剔除
接下来我们看evict
方法:
这个方法分为三大步:
1、 判断是否触发了自我保护机制;
2、 收集所有没有及时发送心跳的实例,放入expiredLeases链表中;
3、 进行实例的剔除;
第一步:
判断当前server是否触发了自我保护机制,如果触发的话方法直接返回。自我保护机制将在下一篇文章中详述。
第二步:
遍历所有app的所有服务实例,调用isExpired
判断服务实例有没有过期,具体方法实现:
注意这个方法的javadoc,这里有一个eureka官方承认的bug,就是我们上一篇文章中提到的lastUpdateTimestamp
的赋值问题导致isExpired
方法判断实例是否过期的时候,过期时间是duration*2
,默认也就是90s*2
。也就是说,一个实例3min都没有发送心跳,才会被剔除,而不是1min30s。
在这里就会用到之前算出来的”补偿时间“了。如果没有考虑到补偿时间的话,那么会造成一些不应该剔除的实例被剔除了。这里建议读者自己算一下就知道会造成的后果了。
选择要剔除的服务实例
执行剔除这里,eureka设计了两种特殊的机制:
1、 每次剔除实例有上限,本次尚未剔除的等下次定时任务再剔除;
2、 随机剔除,避免单个application的服务实例被全部过期;
这两种机制笔者任务都是为了让服务实例过期的尽量平滑。eureka认为:保留可能过期的实例,要比将可用的实例丢失更好。
执行下线逻辑
确定好那些服务实例要剔除后,调用com.netflix.eureka.registry.AbstractInstanceRegistry#internalCancel方法,进行服务下线,这个方法将在《服务下线》的文章中详述。
Over👻.
配置参数
前缀namespace,默认为eureka
服务失效定时剔除任务的间隔时间 (ms)
evictionIntervalTimerInMs = 60 * 1000
服务失效 心跳阈值,配置为0则关闭分批下线的功能
renewalPercentThreshold = 0.85