一级目录
二级目录
三级目录
一 关于Eureka的自我保护模式
1eureka何为引入自我保护模式
首先对Eureka注册中心需要了解的是Eureka各个节点都是平等的,没有ZK中角色的概念, 即使N-1个节点挂掉也不会影响其他节点的正常运行。
默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。
2自我保护机制定义
官方对于自我保护机制的定义:
自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使Eureka集群更加的健壮、稳定的运行。
详细源码解读Renews (last min) < Renews threshold
:
在15分钟(这个十五分钟是一个eruakaserver重新计算心跳阈值的时间窗口getRenewalThresholdUpdateIntervalMs默认值为15分钟
)内超过85%的客户端节点仍然都没有正常的心跳(Renews (last min) < Renews threshold的意思就是注册在某一个eurekaserver中的很多个微服务客户端在上一分钟发送给自己的心跳小于默认值的百分之85
),那么Eureka就认为客户端与注册中心出现了网络故障,如果Renews (last min) < Renews threshold,默认需等待 5 分钟,eureka服务server它等待客户端几分钟(5分钟),以便客户端可以注册他们的信息。(可以通过eureka.server.wait-time-in-ms-when-sync-empty配置),即 5 分钟后如果客户端还没有像服务器注册它的信息,Eureka Server自动进入自我保护机制,你会看到下面的提示信息:
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
此时这个server会出现以下几种情况:
Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。
当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。
因此Eureka Server可以很好的应对因网络故障导致部分节点失联的情况,而不会像ZK那样如果有一半不可用的情况会导致整个集群不可用而变成瘫痪。Eurake有一个配置参数eureka.server.renewalPercentThreshold,定义了renews 和renews threshold的比值,默认值为0.85。当server在15分钟内,比值低于percent,即少了15%的微服务心跳,server会进入自我保护状态,Self-Preservation。在此状态下,server不会删除注册信息,这就有可能导致在调用微服务时,实际上服务并不存在。
这种保护状态实际上是考虑了client和server之间的心跳是因为网络问题,而非服务本身问题,不能简单的删除注册信息.
3关于自我保护机制心跳和阈值的问题的计算和解决问题的方法
①阈值renews threshold
前文说到禁止注册server自己为client,不管server是否禁止自注册,不管集群中有多少个server相互注册,都不会对阈值构成影响,server的阈值(threshold)一直是1。
开启三台server自注册,阈值依然为1
如果client个数为n,阈值为的计算方式就要变成 1+2×n*0.85
关闭自注册,且client的个数为1,则阈值就等于1+210.85=3(阈值计算四舍五入)
②心跳值renews
如果多个server,且开启了自注册,虽然不会对阈值renews threshold
有影响,但是这些就和client一样,是对于其他的server来说就是client
我开了三个server,一个服务客户端,自注册开启和关闭,相关配置如下
我阈值的百分比renews threshold
从默认的0.85调整到了0.6,每分钟的心跳次数eureka.instance.lease-renewal-interval-in-seconds
还是按照系统给的默认值。
#阈值比例
eureka.server.renewal-percent-threshold=0.5
#(30就是默认值,每分钟两次心跳)
eureka.instance.lease-renewal-interval-in-seconds=30
(阈值计算四舍五入)renews threshold:(1+2×1)*0.6=2(自注册和非自注册的阈值都是2)
(实际心跳值)renews:
1)自注册 3+1乘以2=8 三个eurekaserver加一个serverprovider
2)非自注册:1乘以2=2 零个eurekaserver加一个serverprovider
4自我保护机制警告的解决和处理
stackoverflow上,有人给出的建议是:
1、在生产上可以开自注册,部署两个server
2、在本机器上测试的时候,可以把比值调低,比如0.6
#调整保护机制生效的renews 和renews threshold百分比
eureka.server.renewal-percent-threshold=0.6
调整这个参数之后,renews threshold的值会减小到百分之六十
3、或者把自我保护模式关闭,相关配置如下:
#自我保护机制
#eureka.server.enable-self-preservation=false
#自我保护机制关闭之后的配置,告诉服务端,如果你N秒之后还没有接受到我发送的心跳,就代表我"死"了,将我的服务踢出掉。
#eureka.instance.lease-expiration-duration-in-seconds=36
#设置每隔多少秒给服务中心发送一次心跳,证明自己还活着
#eureka.instance.lease-renewal-interval-in-seconds=6
所以综上所述解决这个问题的方式有三种:
1关闭自我保护模式(eureka.server.enable-self-preservation设为false),
不推荐
2降低renewalPercentThreshold的比例(eureka.server.renewal-percent-threshold设置为0.5以下,比如0.49)
不推荐
多个server开启自注册,并相互注册,部署多个 Eureka Server 并开启其客户端行为(eureka.client.register-with-eureka不要设为false,默认为true)
推荐
二eureka参数大全(根据源码属性总结而来)
微服务架构:Eureka参数配置项详解
Eureka涉及到的参数配置项数量众多,它的很多功能都是通过参数配置来实现的,了解这些参数的含义有助于我们更好的应用Eureka的各种功能,下面对Eureka的配置项做具体介绍,供大家参考。
1 Eureka客户端配置
1、RegistryFetchIntervalSeconds
从eureka服务器注册表中获取注册信息的时间间隔(s),默认为30秒
2、InstanceInfoReplicationIntervalSeconds
复制实例变化信息到eureka服务器所需要的时间间隔(s),默认为30秒
3、InitialInstanceInfoReplicationIntervalSeconds
最初复制实例信息到eureka服务器所需的时间(s),默认为40秒
4、EurekaServiceUrlPollIntervalSeconds
询问Eureka服务url信息变化的时间间隔(s),默认为300秒
5、ProxyHost
获取eureka服务的代理主机,默认为null
6、ProxyProxyPort
获取eureka服务的代理端口, 默认为null
7、ProxyUserName
获取eureka服务的代理用户名,默认为null
8、ProxyPassword
获取eureka服务的代理密码,默认为null
9、GZipContent
eureka注册表的内容是否被压缩,默认为true,并且是在最好的网络流量下被压缩
10、EurekaServerReadTimeoutSeconds
eureka需要超时读取之前需要等待的时间,默认为8秒
11、EurekaServerConnectTimeoutSeconds
eureka需要超时连接之前需要等待的时间,默认为5秒
12、BackupRegistryImpl
获取实现了eureka客户端在第一次启动时读取注册表的信息作为回退选项的实现名称
13、EurekaServerTotalConnections
eureka客户端允许所有eureka服务器连接的总数目,默认是200
14、EurekaServerTotalConnectionsPerHost
eureka客户端允许eureka服务器主机连接的总数目,默认是50
15、EurekaServerURLContext
表示eureka注册中心的路径,如果配置为eureka,则为http://x.x.x.x:x/eureka/,在eureka的配置文件中加入此配置表示eureka作为客户端向注册中心注册,从而构成eureka集群。此配置只有在eureka服务器ip地址列表是在DNS中才会用到,默认为null
16、EurekaServerPort
获取eureka服务器的端口,此配置只有在eureka服务器ip地址列表是在DNS中才会用到。默认为null
17、EurekaServerDNSName
获取要查询的DNS名称来获得eureka服务器,此配置只有在eureka服务器ip地址列表是在DNS中才会用到。默认为null
18、UseDnsForFetchingServiceUrls
eureka客户端是否应该使用DNS机制来获取eureka服务器的地址列表,默认为false
19、RegisterWithEureka
实例是否在eureka服务器上注册自己的信息以供其他服务发现,默认为true
20、PreferSameZoneEureka
实例是否使用同一zone里的eureka服务器,默认为true,理想状态下,eureka客户端与服务端是在同一zone下
21、AllowRedirects
服务器是否能够重定向客户端请求到备份服务器。 如果设置为false,服务器将直接处理请求,如果设置为true,它可能发送HTTP重定向到客户端。默认为false
22、LogDeltaDiff
是否记录eureka服务器和客户端之间在注册表的信息方面的差异,默认为false
23、DisableDelta(*)
默认为false
24、fetchRegistryForRemoteRegions
eureka服务注册表信息里的以逗号隔开的地区名单,如果不这样返回这些地区名单,则客户端启动将会出错。默认为null
25、Region
获取实例所在的地区。默认为us-east-1
26、AvailabilityZones
获取实例所在的地区下可用性的区域列表,用逗号隔开。
27、EurekaServerServiceUrls
Eureka服务器的连接,默认为http://XXXX:X/eureka/,但是如果采用DNS方式获取服务地址,则不需要配置此设置。
28、FilterOnlyUpInstances(*)
是否获得处于开启状态的实例的应用程序过滤之后的应用程序。默认为true
29、EurekaConnectionIdleTimeoutSeconds
Eureka服务的http请求关闭之前其响应的时间,默认为30 秒
30、FetchRegistry
此客户端是否获取eureka服务器注册表上的注册信息,默认为true
31、RegistryRefreshSingleVipAddress
此客户端只对一个单一的VIP注册表的信息感兴趣。默认为null
32、HeartbeatExecutorThreadPoolSize(*)
心跳执行程序线程池的大小,默认为5
33、HeartbeatExecutorExponentialBackOffBound(*)
心跳执行程序回退相关的属性,是重试延迟的最大倍数值,默认为10
34、CacheRefreshExecutorThreadPoolSize(*)
执行程序缓存刷新线程池的大小,默认为5
35、CacheRefreshExecutorExponentialBackOffBound
执行程序指数回退刷新的相关属性,是重试延迟的最大倍数值,默认为10
36、DollarReplacement
eureka服务器序列化/反序列化的信息中获取“$”符号的的替换字符串。默认为“_-”
37、EscapeCharReplacement
eureka服务器序列化/反序列化的信息中获取“_”符号的的替换字符串。默认为“__”
38、OnDemandUpdateStatusChange(*)
如果设置为true,客户端的状态更新将会点播更新到远程服务器上,默认为true
39、EncoderName
这是一个短暂的编码器的配置,如果最新的编码器是稳定的,则可以去除,默认为null
40、DecoderName
这是一个短暂的解码器的配置,如果最新的解码器是稳定的,则可以去除,默认为null
41、ClientDataAccept(*)
客户端数据接收
42、Experimental(*)
当尝试新功能迁移过程时,为了避免配置API污染,相应的配置即可投入实验配置部分,默认为null
2 实例微服务端配置
1、InstanceId
此实例注册到eureka服务端的唯一的实例ID,其组成为${spring.application.name}:${spring.application.instance_id:${random.value}}
2、Appname
获得在eureka服务上注册的应用程序的名字,默认为unknow
3、AppGroupName
获得在eureka服务上注册的应用程序组的名字,默认为unknow
4、InstanceEnabledOnit(*)
实例注册到eureka服务器时,是否开启通讯,默认为false
5、NonSecurePort
获取该实例应该接收通信的非安全端口。默认为80
6、SecurePort
获取该实例应该接收通信的安全端口,默认为443
7、NonSecurePortEnabled
该实例应该接收通信的非安全端口是否启用,默认为true
8、SecurePortEnabled
该实例应该接收通信的安全端口是否启用,默认为false
9、LeaseRenewalIntervalInSeconds
eureka客户需要多长时间发送心跳给eureka服务器,表明它仍然活着,默认为30 秒
10、LeaseExpirationDurationInSeconds
Eureka服务器在接收到实例的最后一次发出的心跳后,需要等待多久才可以将此实例删除,默认为90秒
11、VirtualHostName
此实例定义的虚拟主机名,其他实例将通过使用虚拟主机名找到该实例。
12、SecureVirtualHostName
此实例定义的安全虚拟主机名
13、ASGName(*)
与此实例相关联 AWS自动缩放组名称。此项配置是在AWS环境专门使用的实例启动,它已被用于流量停用后自动把一个实例退出服务。
14、HostName
与此实例相关联的主机名,是其他实例可以用来进行请求的准确名称
15、MetadataMap(*)
获取与此实例相关联的元数据(key,value)。这个信息被发送到eureka服务器,其他实例可以使用。
16、DataCenterInfo(*)
该实例被部署在数据中心
17、IpAddress
获取实例的ip地址
18、StatusPageUrlPath(*)
获取此实例状态页的URL路径,然后构造出主机名,安全端口等,默认为/info
19、StatusPageUrl(*)
获取此实例绝对状态页的URL路径,为其他服务提供信息时来找到这个实例的状态的路径,默认为null
20、HomePageUrlPath(*)
获取此实例的相关主页URL路径,然后构造出主机名,安全端口等,默认为/
21、HomePageUrl(*)
获取此实例的绝对主页URL路径,为其他服务提供信息时使用的路径,默认为null
22、HealthCheckUrlPath
获取此实例的相对健康检查URL路径,默认为/health
23、HealthCheckUrl
获取此实例的绝对健康检查URL路径,默认为null
24、SecureHealthCheckUrl
获取此实例的绝对安全健康检查网页的URL路径,默认为null
25、DefaultAddressResolutionOrder
获取实例的网络地址,默认为[]
26、Namespace
获取用于查找属性的命名空间,默认为eureka
3 Eureka服务端配置
1、AWSAccessId
获取aws访问的id,主要用于弹性ip绑定,此配置是用于aws上的,默认为null
2、AWSSecretKey
获取aws私有秘钥,主要用于弹性ip绑定,此配置是用于aws上的,默认为null
3、EIPBindRebindRetries
获取服务器尝试绑定到候选的EIP的次数,默认为3
4、EIPBindingRetryIntervalMsWhenUnbound(*)
服务器检查ip绑定的时间间隔,单位为毫秒,默认为1 * 60 * 1000
5、EIPBindingRetryIntervalMs
与上面的是同一作用,仅仅是稳定状态检查,默认为5 * 60 * 1000
6、EnableSelfPreservation
自我保护模式,当出现出现网络分区、eureka在短时间内丢失过多客户端时,会进入自我保护模式,即一个服务长时间没有发送心跳,eureka也不会将其删除,默认为true
7、RenewalPercentThreshold(*)
阈值因子,默认是0.85,如果阈值比最小值大,则自我保护模式开启
8、RenewalThresholdUpdateIntervalMs
阈值更新的时间间隔,单位为毫秒,默认为15 * 60 * 1000
9、PeerEurekaNodesUpdateIntervalMs(*)
集群里eureka节点的变化信息更新的时间间隔,单位为毫秒,默认为10 * 60 * 1000
10、EnableReplicatedRequestCompression
复制的数据在发送请求时是否被压缩,默认为false
11、NumberOfReplicationRetries
获取集群里服务器尝试复制数据的次数,默认为5
12、PeerEurekaStatusRefreshTimeIntervalMs
服务器节点的状态信息被更新的时间间隔,单位为毫秒,默认为30 * 1000
13、WaitTimeInMsWhenSyncEmpty(*)
在Eureka服务器获取不到集群里对等服务器上的实例时,需要等待的时间,单位为毫秒,默认为1000 * 60 * 5
14、PeerNodeConnectTimeoutMs
连接对等节点服务器复制的超时的时间,单位为毫秒,默认为200
15、PeerNodeReadTimeoutMs
读取对等节点服务器复制的超时的时间,单位为毫秒,默认为200
16、PeerNodeTotalConnections
获取对等节点上http连接的总数,默认为1000
17、PeerNodeTotalConnectionsPerHost(*)
获取特定的对等节点上http连接的总数,默认为500
18、PeerNodeConnectionIdleTimeoutSeconds(*)
http连接被清理之后服务器的空闲时间,默认为30秒
19、RetentionTimeInMSInDeltaQueue(*)
客户端保持增量信息缓存的时间,从而保证不会丢失这些信息,单位为毫秒,默认为3 * 60 * 1000
20、DeltaRetentionTimerIntervalInMs
清理任务程序被唤醒的时间间隔,清理过期的增量信息,单位为毫秒,默认为30 * 1000
21、EvictionIntervalTimerInMs
过期实例应该启动并运行的时间间隔,单位为毫秒,默认为60 * 1000
22、ASGQueryTimeoutMs(*)
查询AWS上ASG(自动缩放组)信息的超时值,单位为毫秒,默认为300
23、ASGUpdateIntervalMs
从AWS上更新ASG信息的时间间隔,单位为毫秒,默认为5 * 60 * 1000
24、ASGCacheExpiryTimeoutMs(*)
缓存ASG信息的到期时间,单位为毫秒,默认为10 * 60 * 1000
25、ResponseCacheAutoExpirationInSeconds(*)
当注册表信息被改变时,则其被保存在缓存中不失效的时间,默认为180秒
26、ResponseCacheUpdateIntervalMs(*)
客户端的有效负载缓存应该更新的时间间隔,默认为30 * 1000毫秒
27、UseReadOnlyResponseCache(*)
目前采用的是二级缓存策略,一个是读写高速缓存过期策略,另一个没有过期只有只读缓存,默认为true,表示只读缓存
28、DisableDelta(*)
增量信息是否可以提供给客户端看,默认为false
29、MaxIdleThreadInMinutesAgeForStatusReplication(*)
状态复制线程可以保持存活的空闲时间,默认为10分钟
30、MinThreadsForStatusReplication
被用于状态复制的线程的最小数目,默认为1
31、MaxThreadsForStatusReplication
被用于状态复制的线程的最大数目,默认为1
32、MaxElementsInStatusReplicationPool
可允许的状态复制池备份复制事件的最大数量,默认为10000
33、SyncWhenTimestampDiffers
当时间变化实例是否跟着同步,默认为true
34、RegistrySyncRetries
当eureka服务器启动时尝试去获取集群里其他服务器上的注册信息的次数,默认为5
35、RegistrySyncRetryWaitMs
当eureka服务器启动时获取其他服务器的注册信息失败时,会再次尝试获取,期间需要等待的时间,默认为30 * 1000毫秒
36、MaxElementsInPeerReplicationPool(*)
复制池备份复制事件的最大数量,默认为10000
37、MaxIdleThreadAgeInMinutesForPeerReplication(*)
复制线程可以保持存活的空闲时间,默认为15分钟
38、MinThreadsForPeerReplication(*)
获取将被用于复制线程的最小数目,默认为5
39、MaxThreadsForPeerReplication
获取将被用于复制线程的最大数目,默认为20
40、MaxTimeForReplication(*)
尝试在丢弃复制事件之前进行复制的时间,默认为30000毫秒
41、PrimeAwsReplicaConnections(*)
对集群中服务器节点的连接是否应该准备,默认为true
42、DisableDeltaForRemoteRegions(*)
增量信息是否可以提供给客户端或一些远程地区,默认为false
43、RemoteRegionConnectTimeoutMs(*)
连接到对等远程地eureka节点的超时时间,默认为1000毫秒
44、RemoteRegionReadTimeoutMs(*)
获取从远程地区eureka节点读取信息的超时时间,默认为1000毫秒
45、RemoteRegionTotalConnections
获取远程地区对等节点上http连接的总数,默认为1000
46、RemoteRegionTotalConnectionsPerHost
获取远程地区特定的对等节点上http连接的总数,默认为500
47、RemoteRegionConnectionIdleTimeoutSeconds
http连接被清理之后远程地区服务器的空闲时间,默认为30秒
48、GZipContentFromRemoteRegion(*)
eureka服务器中获取的内容是否在远程地区被压缩,默认为true
49、RemoteRegionUrlsWithName
针对远程地区发现的网址域名的map
50、RemoteRegionUrls
远程地区的URL列表
51、RemoteRegionAppWhitelist(*)
必须通过远程区域中检索的应用程序的列表
52、RemoteRegionRegistryFetchInterval
从远程区域取出该注册表的信息的时间间隔,默认为30秒
53、RemoteRegionFetchThreadPoolSize
用于执行远程区域注册表请求的线程池的大小,默认为20
54、RemoteRegionTrustStore
用来合格请求远程区域注册表的信任存储文件,默认为空
55、RemoteRegionTrustStorePassword
获取偏远地区信任存储文件的密码,默认为“changeit”
56、disableTransparentFallbackToOtherRegion(*)
如果在远程区域本地没有实例运行,对于应用程序回退的旧行为是否被禁用, 默认为false
57、BatchReplication(*)
表示集群节点之间的复制是否为了网络效率而进行批处理,默认为false
58、LogIdentityHeaders(*)
Eureka服务器是否应该登录clientAuthHeaders,默认为true
59、RateLimiterEnabled
限流是否应启用或禁用,默认为false
60、RateLimiterThrottleStandardClients
是否对标准客户端进行限流,默认false
61、RateLimiterPrivilegedClients(*)
认证的客户端列表,这里是除了标准的eureka Java客户端。
62、RateLimiterBurstSize(*)
速率限制的burst size ,默认为10,这里用的是令牌桶算法
63、RateLimiterRegistryFetchAverageRate(*)
速率限制器用的是令牌桶算法,此配置指定平均执行注册请求速率,默认为500
64、RateLimiterFullFetchAverageRate(*)
速率限制器用的是令牌桶算法,此配置指定平均执行请求速率,默认为100
65、ListAutoScalingGroupsRoleName(*)
用来描述从AWS第三账户的自动缩放组中的角色名称,默认为“ListAutoScalingGroups”
66、JsonCodecName(*)
如果没有设置默认的编解码器将使用全JSON编解码器,获取的是编码器的类名称
67、XmlCodecName(*)
如果没有设置默认的编解码器将使用xml编解码器,获取的是编码器的类名称
68、BindingStrategy(*)
获取配置绑定EIP或Route53的策略。
69、Route53DomainTTL(*)
用于建立route53域的ttl,默认为301
70、Route53BindRebindRetries(*)
服务器尝试绑定到候选Route53域的次数,默认为3
71、Route53BindingRetryIntervalMs(*)
服务器应该检查是否和Route53域绑定的时间间隔,默认为5 * 60 * 1000毫秒
72、Experimental(*)
当尝试新功能迁移过程时,为了避免配置API污染,相应的配置即可投入实验配置部分,默认为null
以上是Eureka配置项的详细说明,分为Eureka客户端配置、Eureka服务端配置和微服务端配置,一共100多项,其中有很多配置参数并不需要我们去修改,使用默认的就好,有些跟我们业务相关的配置参数可根据需要自行设置。
三 eureka-server集群的自我保护机制的源码解析
默认情况下,当EurekaServer在一定时间内(默认90秒)没有接收到某个客户端实例的心跳,EurekaServer将会注销该实例。但是当网络分区故障发生时,客户端与EurekaServer之间无法正常通信,此时不应该注销客户端。Eureka通过“自我保护机制”来解决这个问题:当EurekaServer短时间内丢失过多客户端时,这个节点就会进入自我保护模式。在自我保护模式下,EurekaServer不会剔除任何客户端。当网络故障恢复后,该节点会自动退出自我保护模式
自我保护机制的实现是基于维护服务注册表的类AbstractInstanceRegistry中的2个变量来维护的
/**
- 期望最小每分钟续租次数
*/
protected volatile int numberOfRenewsPerMinThreshold;
/**
- 期望最大每分钟续租次数
*/
protected volatile int expectedNumberOfRenewsPerMin;
服务端初始化
服务端的启动文章可以看这篇文章:EurekaServer自动装配及启动流程解析
在服务端启动、从其他集羣同步完信息后执行了一个方法:openForTraffic
public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) {
this.expectedNumberOfRenewsPerMin = count * 2;
this.numberOfRenewsPerMinThreshold =
(int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());
}
期望每分钟最大续租次数为:当前服务端已经注册的客户端的数量乘2,为啥呢,因为默认Eureka的续约是30秒
期望每分钟最小续租次数为:最大续租次数乘续租百分比,默认续租百分比是0.85,也就是说当某个时间窗内如果存在超过百分之十五的客户端没有再续租的话则开启自我保护模式
自我保护模式的定时任务
DefaultEurekaServerContext类中有一个initialize方法,这个方法在执行过程中会启动一个定时任务
@PostConstruct
@Override
public void initialize() {
logger.info("Initializing ...");
peerEurekaNodes.start();
try {
registry.init(peerEurekaNodes);
} catch (Exception e) {
throw new RuntimeException(e);
}
logger.info("Initialized");
}
public void init(PeerEurekaNodes peerEurekaNodes) throws Exception {
this.numberOfReplicationsLastMin.start();
this.peerEurekaNodes = peerEurekaNodes;
initializedResponseCache();
scheduleRenewalThresholdUpdateTask();
initRemoteRegionRegistry();
try {
Monitors.registerObject(this);
} catch (Throwable e) {
logger.warn("Cannot register the JMX monitor for the InstanceRegistry :", e);
}
}
scheduleRenewalThresholdUpdateTask这个定时任务就是跟自我保护模式相关的了
private void scheduleRenewalThresholdUpdateTask() {
timer.schedule(new TimerTask() {
@Override
public void run() {
updateRenewalThreshold();
}
}, serverConfig.getRenewalThresholdUpdateIntervalMs(),
serverConfig.getRenewalThresholdUpdateIntervalMs());
}
其中getRenewalThresholdUpdateIntervalMs默认值是15分钟
private void updateRenewalThreshold() {
try {
// 1. 计算应用实例数
Applications apps = eurekaClient.getApplications();
int count = 0;
for (Application app : apps.getRegisteredApplications()) {
for (InstanceInfo instance : app.getInstances()) {
if (this.isRegisterable(instance)) {
++count;
}
}
}
// 2. 计算expectedNumberOfRenewsPerMin 、 numberOfRenewsPerMinThreshold 参数
synchronized (lock) {
if ((count * 2) > (serverConfig.getRenewalPercentThreshold() * numberOfRenewsPerMinThreshold)
|| (!this.isSelfPreservationModeEnabled())) {
this.expectedNumberOfRenewsPerMin = count * 2;
this.numberOfRenewsPerMinThreshold = (int) ((count * 2) * serverConfig.getRenewalPercentThreshold());
}
}
logger.info("Current renewal threshold is : {}", numberOfRenewsPerMinThreshold);
} catch (Throwable e) {
logger.error("Cannot update renewal threshold", e);
}
}
分为2步,第一步就不说了,看第二步
当最大续租数量大于最小续租数量时或者没有开启自我保护模式时可以重新计算两个值,否则不能重新计算
客户端注册
public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
synchronized (lock) {
if (this.expectedNumberOfRenewsPerMin > 0) {
this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin + 2;
this.numberOfRenewsPerMinThreshold =
(int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());
}
}
}
每当有一个实例注册上来时,两个参数都要重新计算,最大期望续租数量+2同样是因为默认1分钟续租2次
客户端下线
public boolean cancel(final String appName, final String id,
final boolean isReplication) {
synchronized (lock) {
if (this.expectedNumberOfRenewsPerMin > 0) {
this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin - 2;
this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());
}
}
}
于注册的处理逻辑恰好相反
开启自我保护模式
之前在Eureka客户端续约及服务端过期租约清理源码解析一文的租约过期清理解析过程中省略了关于自我保护模式的判断,现在再看一下。这个判断在租约过期处理方法的开头:
public void evict(long additionalLeaseMs) {
logger.debug("Running the evict task");
if (!isLeaseExpirationEnabled()) {
logger.debug("DS: lease expiration is currently disabled.");
return;
}
//....
}
详细内容在isLeaseExpirationEnabled中
public boolean isLeaseExpirationEnabled() {
if (!isSelfPreservationModeEnabled()) {
return true;
}
return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold;
}
public boolean isSelfPreservationModeEnabled() {
return serverConfig.shouldEnableSelfPreservation();
}
public long getNumOfRenewsInLastMin() {
return renewsLastMin.getCount();
}
第一个if是判断是否开启自我保护模式
最后的return则是如果当前最小续租次数大于0,并且最近续约实例数量大于最小期待续租数量
.