很多时候,服务实例并不是主动放delete请求下线的,而是宕机或者其他情况,并不会去通知注册中心(eureka server)。
eureka server是靠着心跳(续约)实现服务实例故障的自动感知以及服务实例自动摘除机制。
eureka server在EurekaBootStrap中启动初始化的时候,执行了一行代码registry.openForTraffic(applicationInfoManager, registryCount);
这个方法的最后一行,调用了父类的postInit()方法
这里延迟60s启动,然后每隔60s运行一次后台线程任务EvictionTask。
故障感知的流程:
(1)获取一个补偿时间,这是为了避免EvictionTask两次调度的间隔超过了60s(比如说系统时钟出错)
(2)遍历注册表中所有的服务实例,然后调用Lease的isExpired()方法,来判断当前这个服务实例的租约是否过期了,是否失效了,服务实例故障了,如果是故障的服务实例,加入一个列表。如果上次的心跳到现在间隔了90s * 2 = 180s,3分钟,才会认为是故障了。为什么是90s * 2?
本来在isExpired的逻辑中,是否超时的计算是上次心跳更新时间+duration(90s) + 补偿时间
但是上次心跳更新时间(lastUpdateTimestamp)的计算中已经添加过一次duration了
所以这里是两个duration=180s,3分钟。
也就是说如果一个服务实例心跳停了,在过去三分钟+补偿时间后 ,注册中心才能感知到。这时,将心跳超时的服务实例的注册信息Lease都添加到一个超时集合中去。
(3)这里并不会将超时的服务实例全部摘除,考虑到系统gc等情况,这里会根据本地服务实例的总数以及serverConfig.getRenewalPercentThreshold()(0.85)计算出最多可摘除服务实例数evictionLimit,之后比较evictionLimit和超时服务实例的数量,选择其中较小的作为需要摘除的服务实例数toEvict。
(4)下次EvictionTask再次执行的时候,会再次摘除,分批摘取机制
(5)在摘除的时候,是从故障实例中随机挑选本次可以摘除的数量的服务实例,来摘除,随机摘取机制
(6)服务实例的摘除其实就是调用下线的internalCancel()方法。