摘要: 原创出处 http://www.iocoder.cn/Eureka/instance-registry-self-preservation/ 「芋道源码」欢迎转载,保留摘要,谢谢!java
本文主要基于 Eureka 1.8.X 版本node
🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:git
- RocketMQ / MyCAT / Sharding-JDBC 全部源码分析文章列表
- RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
- 您对于源码的疑问每条留言都将获得认真回复。甚至不知道如何读源码也能够请教噢。
- 新的源码解析文章实时收到通知。每周更新一篇左右。
- 认真的源码交流微信群。
本文主要分享 自我保护机制,为应用实例过时下线作铺垫。github
推荐 Spring Cloud 书籍:spring
推荐 Spring Cloud 视频:segmentfault
自我保护机制定义以下:微信
FROM 周立 —— 《理解Eureka的自我保护模式》
当Eureka Server节点在短期内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,再也不删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。网络
为何使用自动保护机制 ?你也能够从周立兄的这篇文章获得答案,这里笔者就不一本正经的胡说八道了。架构
首先,咱们来看下在自动保护机制里扮演重要角色的两个变量:app
// AbstractInstanceRegistry.java /** * 指望最小每分钟续租次数 */ protected volatile int numberOfRenewsPerMinThreshold; /** * 指望最大每分钟续租次数 */ protected volatile int expectedNumberOfRenewsPerMin;
expectedNumberOfRenewsPerMin
,指望最大每分钟续租次数。numberOfRenewsPerMinThreshold
,指望最小每分钟续租次数。当每分钟心跳次数( renewsLastMin
) 小于 numberOfRenewsPerMinThreshold
时,而且开启自动保护模式开关( eureka.enableSelfPreservation = true
) 时,触发自动保护机制,再也不自动过时租约,实现代码以下:
// AbstractInstanceRegistry.java public void evict(long additionalLeaseMs) { if (!isLeaseExpirationEnabled()) { logger.debug("DS: lease expiration is currently disabled."); return; } // ... 省略过时租约逻辑 } // PeerAwareInstanceRegistryImpl.java @Override public boolean isLeaseExpirationEnabled() { if (!isSelfPreservationModeEnabled()) { // The self preservation mode is disabled, hence allowing the instances to expire. return true; } return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold; }
计算公式以下:
expectedNumberOfRenewsPerMin
= 当前注册的应用实例数 x
2numberOfRenewsPerMinThreshold
= expectedNumberOfRenewsPerMin
*
续租百分比( eureka.renewalPercentThreshold
)为何乘以 2
默认状况下,注册的应用实例每半分钟续租一次,那么一分钟心跳两次,所以 x 2 。
这块会有一些硬编码的状况,所以不太建议修改应用实例的续租频率。
为何乘以续租百分比
低于这个百分比,意味着开启自我保护机制。
默认状况下,eureka.renewalPercentThreshold = 0.85
。
若是你真的调整了续租频率,能够等比去续租百分比,以保证合适的触发自我保护机制的阀值。另外,你须要注意,续租频率是 Client 级别,续租百分比是 Server 级别。
目前有四个地方会计算 numberOfRenewsPerMinThreshold
、 expectedNumberOfRenewsPerMin
,咱们逐小节来看。
Eureka-Server 在启动时,从 Eureka-Server 集群获取注册信息,并首次初始化 numberOfRenewsPerMinThreshold
、 expectedNumberOfRenewsPerMin
。实现代码以下:
// EurekaBootStrap.java protected void initEurekaServerContext() throws Exception { // ... 省略其它代码 // 【2.2.10】从其余 Eureka-Server 拉取注册信息 // Copy registry from neighboring eureka node int registryCount = registry.syncUp(); registry.openForTraffic(applicationInfoManager, registryCount); // ... 省略其它代码 } // PeerAwareInstanceRegistryImpl.java @Override public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) { // Renewals happen every 30 seconds and for a minute it should be a factor of 2. this.expectedNumberOfRenewsPerMin = count * 2; this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold()); // ... 省略其它代码 }
Eureka-Server 定时从新计算 numberOfRenewsPerMinThreshold
、expectedNumberOfRenewsPerMin
。实现代码以下:
// PeerAwareInstanceRegistryImpl.java private void scheduleRenewalThresholdUpdateTask() { timer.schedule(new TimerTask() { @Override public void run() { updateRenewalThreshold(); } }, serverConfig.getRenewalThresholdUpdateIntervalMs(), serverConfig.getRenewalThresholdUpdateIntervalMs()); } // AbstractInstanceRegistry.java /** * 自我保护机锁 * * 当计算以下参数时使用: * 1. {@link #numberOfRenewsPerMinThreshold} * 2. {@link #expectedNumberOfRenewsPerMin} */ protected final Object lock = new Object(); private void updateRenewalThreshold() { try { // 计算 应用实例数 Applications apps = eurekaClient.getApplications(); int count = 0; for (Application app : apps.getRegisteredApplications()) { for (InstanceInfo instance : app.getInstances()) { if (this.isRegisterable(instance)) { ++count; } } } // 计算 expectedNumberOfRenewsPerMin 、 numberOfRenewsPerMinThreshold 参数 synchronized (lock) { // Update threshold only if the threshold is greater than the // current expected threshold of if the self preservation is disabled. 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); } }
eureka.renewalThresholdUpdateIntervalMs
参数,定时从新计算。默认,15 分钟。!this.isSelfPreservationModeEnabled()
:当未开启自我保护机制时,每次都进行从新计算。事实上,这两个参数不单单自我保护机制会使用到,配合 Netflix Servo 实现监控信息采集 numberOfRenewsPerMinThreshold
、expectedNumberOfRenewsPerMin
。(count * 2) > (serverConfig.getRenewalPercentThreshold() * numberOfRenewsPerMinThreshold)
:当开启自我保护机制时,应用实例每分钟最大心跳数( count * 2
) 小于指望最小每分钟续租次数( serverConfig.getRenewalPercentThreshold() * numberOfRenewsPerMinThreshold
),不从新计算。若是从新计算,自动保护机制会每次定时执行后失效。应用实例注册时,增长 numberOfRenewsPerMinThreshold
、expectedNumberOfRenewsPerMin
。实现代码以下:
// public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) { // ... 省略无关代码 // The lease does not exist and hence it is a new registration // 【自我保护机制】增长 `numberOfRenewsPerMinThreshold` 、`expectedNumberOfRenewsPerMin` synchronized (lock) { if (this.expectedNumberOfRenewsPerMin > 0) { // Since the client wants to cancel it, reduce the threshold // (1 // for 30 seconds, 2 for a minute) this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin + 2; this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold()); } } // ... 省略无关代码 }
应用实例下线时,减小 numberOfRenewsPerMinThreshold
、expectedNumberOfRenewsPerMin
。实现代码以下:
// PeerAwareInstanceRegistryImpl.java @Override public boolean cancel(final String appName, final String id, final boolean isReplication) { // ... 省略无关代码 synchronized (lock) { if (this.expectedNumberOfRenewsPerMin > 0) { // Since the client wants to cancel it, reduce the threshold (1 for 30 seconds, 2 for a minute) this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin - 2; this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold()); } } // ... 省略无关代码 }
😈 终于完整理解 Eureka-Server 自我保护机制,知足。噶~~~~~~
推荐另外一篇 Eureka-Server 自我保护机制源码分析文章:《理解eureka的自我保护机制》 。
胖友,分享个人公众号( 芋道源码 ) 给你的胖友可好?