摘要: 原創出處 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的自我保護機制》 。
胖友,分享個人公衆號( 芋道源碼 ) 給你的胖友可好?