摘要: 原创出处 http://www.iocoder.cn/Eureka/instance-registry-renew/ 「芋道源码」欢迎转载,保留摘要,谢谢!
本文主要基于 Eureka 1.8.X 版本
本文主要分享 Eureka-Client 向 Eureka-Server 续租应用实例的过程。
推荐 Spring Cloud 书籍:
Eureka-Client 向 Eureka-Server 发起注册应用实例成功后获得租约 ( Lease )。
Eureka-Client 固定间隔向 Eureka-Server 发起续租( renew ),避免租约过期。
默认情况下,租约有效期为 90 秒,续租频率为 30 秒。两者比例为 1 : 3 ,保证在网络异常等情况下,有三次重试的机会。
Eureka-Client 在初始化过程中,创建心跳线程,固定间隔向 Eureka-Server 发起续租( renew )。实现代码如下:
|
scheduler
,定时任务服务,用于定时触发心跳( 续租 )。细心如你,会发现任务提交的方式是 ScheduledExecutorService#schedule(...)
方法,只延迟执行一次心跳,说好的固定频率执行心跳呢!!!答案在 「2.3 TimedSupervisorTask」 揭晓。heartbeatExecutor
,心跳任务执行线程池。为什么有 scheduler
的情况下,还有 heartbeatExecutor
???答案也在 「2.3 TimedSupervisorTask」 揭晓。com.netflix.discovery.DiscoveryClient.HeartbeatThread
,心跳线程,实现执行 Eureka-Client 向 Eureka-Server 发起续租( renew )请求。实现代码如下:
|
调用 #renew
方法,执行续租逻辑。实现代码如下:
|
调用 AbstractJerseyEurekaHttpClient#sendHeartBeat(...)
方法,发起续租请求,实现代码如下:
|
apps/${APP_NAME}/${INSTANCE_INFO_ID}
接口,参数为 status
、lastDirtyTimestamp
、overriddenstatus
,实现续租。调用 AbstractJerseyEurekaHttpClient#register(...)
方法,当 Eureka-Server 不存在租约时,重新发起注册,在《Eureka 源码解析 —— 应用实例注册发现 (一)之注册》有详细解析。
com.netflix.discovery.TimedSupervisorTask
,监管定时任务的任务。
A supervisor task that schedules subtasks while enforce a timeout.
创建 TimedSupervisorTask 代码如下:
|
scheduler
,定时任务服务,用于定时【发起】子任务。executor
,执行子任务线程池,用于【提交】子任务执行。task
,子任务。timeoutMillis
,子任务执行超时时间,单位:毫秒。delay
,当前子任务执行频率,单位:毫秒。值等于 timeout
参数。maxDelay
,最大子任务执行频率,单位:毫秒。值等于 timeout * expBackOffBound
参数。scheduler
初始化延迟执行 TimedSupervisorTask 。task
到 executor
执行任务。
task
执行正常,TimedSupervisorTask 再次提交自己到scheduler
延迟 timeoutMillis
执行。task
执行超时,重新计算延迟时间( 不允许超过 maxDelay
),再次提交自己到scheduler
延迟执行。实现代码如下:
|
task
到执行子任务线程池 executor
。task
执行完成或执行超时。task
执行完成,设置下一次执行延迟 delay
。task
执行超时,重新计算下一次执行延迟 delay
。计算公式为 Math.min(maxDelay, currentDelay * 2)
。如果多次超时,超时时间不断乘以 2 ,不允许超过最大延迟时间( maxDelay
)。com.netflix.eureka.resources.InstanceResource
,处理单个应用实例信息的请求操作的 Resource ( Controller )。
续租应用实例信息的请求,映射 InstanceResource#renewLease()
方法,实现代码如下:
|
第 8 至 9 行 :调用 PeerAwareInstanceRegistryImpl#renew(...)
方法,续租。实现代码如下:
|
AbstractInstanceRegistry#renew(...)
方法,注册应用实例信息。第 11 至 16 行 :续租失败,返回 404 响应。当 Eureka-Client 收到 404 响应后,会重新发起 InstanceInfo 的注册。
第 18 至 30 行 :比较请求的 lastDirtyTimestamp
和 Server 的 InstanceInfo 的 lastDirtyTimestamp
属性差异,需要配置 eureka.syncWhenTimestampDiffers = true
( 默认开启 )。
第 23 行 :调用 #validateDirtyTimestamp(...)
方法,比较 lastDirtyTimestamp
的差异。实现代码如下:
|
lastDirtyTimestamp
较大,意味着请求方( 可能是 Eureka-Client ,也可能是 Eureka-Server 集群内的其他 Server )存在 InstanceInfo 和 Eureka-Server 的 InstanceInfo 的数据不一致,返回 404 响应。请求方收到 404 响应后重新发起注册。lastDirtyTimestamp
较大,并且请求方为 Eureka-Client,续租成功,返回 200 成功响应。lastDirtyTimestamp
一致,返回 200 成功响应。第 31 至 33 行 :续租成功,返回 200 成功响应。
调用 AbstractInstanceRegistry#renew(...)
方法,续租应用实例信息,实现代码如下:
|
false
)。UNKNOWN
,无法续约,返回 false
。在《应用实例注册发现 (八)之覆盖状态》详细解析。第 40 至 41 行 :新增续租每分钟次数( renewsLastMin
)。com.netflix.eureka.util.MeasuredRate
,速度测量类,实现代码如下:
|
timer
,定时器,负责每个 sampleInterval
间隔重置当前次数( currentBucket
),并将原当前次数设置到上一个次数( lastBucket
)。#increment()
方法,返回当前次数( currentBucket
)。#getCount()
方法,返回上一个次数( lastBucket
)。renewsLastMin
有如下用途:
第 42 至 43 行 :调用 Lease#renew()
方法,设置租约最后更新时间( 续租 ),实现代码如下:
|
第 44 行 :返回续租成功( true
)。