Spring Cloud Eureka原理分析(二):續租、下線、自我保護機制和自動清理(服務端)

續租、下線等操做比較直觀,實際上也不復雜。讓咱們本身想一想它們大概會在服務端有什麼操做。java

  • renew: 更新Lease的lastUpdateTimestamp, 更新一下InstanceInfo的最新狀態。而後調用其餘同伴節點的renew接口。
  • cancel:把lease從registry中移除,設置lease的evictionTimestamp,而後設置InstanceInfo爲已刪除。而後把lease加入到recentlyChangedQueue中。最後調用同伴節點的cancel接口。

—— 服務端確實作了差很少這些事情。所以爲了避免影響其餘重要事件,這裏再也不繼續深刻。數組

自我保護機制

Eureka選擇作一個支持AP的系統(CAP定理)。其實很好理解,當發生大量應用實例再也不renew時,服務端認爲發生了網絡分區。Eureka爲了保證高可用,再也不踢掉已經到期的lease,從而讓依賴於該服務端實例的client端仍然能正常進行服務發現(儘管存在服務實例確實掛掉的可能,即犧牲了一致性)。bash

在Spring Cloud Eureka的Home頁面上面常常會見到這個警告,就是啓用了自我保護機制。網絡

關於這個問題網上的解釋也不少了,下面這篇文章提供了幾幅有用的圖:ide

The Mystery of Eureka Self-Preservation編碼

實現細節

這篇文章有全部你想要的。 Eureka 源碼解析 —— 應用實例註冊發現(四)之自我保護機制spa

搬過來一些重點:code

1. 觸發條件

看代碼cdn

// 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;
}
複製代碼

當每分鐘心跳次數( renewsLastMin ) 小於 numberOfRenewsPerMinThreshold 時,而且開啓自動保護模式開關( eureka.server.enableSelfPreservation = true ) 時,觸發自動保護機制,再也不自動過時租約。server

其中:

2. 計算公式

  • numberOfRenewsPerMinThreshold = expectedNumberOfRenewsPerMin * 續租百分比( eureka.server.renewalPercentThreshold, 默認0.85 )
  • expectedNumberOfRenewsPerMin = 當前註冊的應用實例數 x 2

爲何乘以 2:

默認狀況下,註冊的應用實例每半分鐘續租一次,那麼一分鐘心跳兩次,所以 x 2 。

這裏有硬編碼的狀況,所以修改應用實例的續租頻率會讓計算不太準。不過,自我保護機制我比較懷疑它的重要性,該調還得調。

3. 計算時機

有四個地方會從新計算numberOfRenewsPerMinThresholdexpectedNumberOfRenewsPerMin

  • Eureka-Server 啓動時。先從相鄰節點同步registry中的信息,獲得已註冊的實例數量,而後套公式計算。
  • 定時從新計算。計算方法同上相似。頻率由eureka.server.renewalThresholdUpdateIntervalMs控制。
  • 應用實例註冊時,增長了註冊實例數,因此要重算。
  • 實例下線時,同理。

自動清理機制

清理過時lease的也是一個定時任務EvictionTask,頻率由eureka.server.evictionIntervalTimerInMs,默認爲60秒。

1. EvictionTask代碼

class EvictionTask extends TimerTask {

   @Override
   public void run() {
       try {
           // 獲取 補償時間毫秒數
           long compensationTimeMs = getCompensationTimeMs();
           logger.info("Running the evict task with compensationTime {}ms", compensationTimeMs);
           // 清理過時租約邏輯
           evict(compensationTimeMs);
       } catch (Throwable e) {
           logger.error("Could not run the evict task", e);
       }
   }
}
複製代碼

那個getCompensationTimeMs()方法的意思是,定時器可能比設定的時間更晚觸發,緣由多是GC等。假設原本要在上一次執行後60s再次觸發,但由於GC在第70秒才被觸發,這時去檢查有沒有lease有沒有超期無renew不能用70s來算,而應該還用60s來算,由於在這10s的GC時間中,極可能此服務端沒法處理註冊請求。補償的10s就是這個意思,就是容許實例多超期這10s。

2. 清理邏輯

evict(compensationTime)又比較長,下面分段分析。

  1. 判斷是否是啓用了自我保護機制,如是則再也不清理。
  2. 遍歷registry中的全部實例,比較當前時間和lastUpdatedTime + duration + compensationTime,若是前者大,說明已過時。
  3. 計算最大可清理的實例數量。不得讓清理後的實例數量低於當前數量 * eureka.server.renewalPercentThreshold,默認又是0.85。這個閾值仍是很高的,若是有大量實例過時,就須要分多批執行才能清理完。
  4. 隨機清理過時的租約。因爲租約是按照應用順序添加到數組,經過隨機的方式,儘可能避免單個應用被所有過時。
  5. 最後,調用internalCancel處理每一個要被清理的lease,這個方法就是前面的cancel階段會調的。

這個自動清理彷佛沒有告知同伴節點,你們各作各的。可見Eureka的一致性是蠻低的。

相關文章
相關標籤/搜索