摘要: 原創出處
http://www.iocoder.cn/Eureka/instance-registry-evict/ 「芋道源碼」歡迎轉載,保留摘要,謝謝!
本文主要基於 Eureka 1.8.X 版本
- 1. 概述
- 2. 爲什麼需要過期
- 3. EvictionTask
- 4. 過期邏輯
- 請支持正版。下載盜版,等於主動編寫低級 BUG 。
- 程序猿DD —— 《Spring Cloud微服務實戰》
- 周立 —— 《Spring Cloud與Docker微服務架構實戰》
- 兩書齊買,京東包郵。
- 配置
eureka.evictionIntervalTimerInMs
,清理租約過期任務執行頻率,單位:毫秒。默認,60000 毫秒。 EvictionTask 實現代碼如下:
class EvictionTask extends TimerTask {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);}}}調用
#compensationTimeMs()
方法,獲得補償時間毫秒數。計算公式 = 當前時間 - 最後任務執行時間 - 任務執行頻率。爲什麼需要補償時間毫秒數,在 「4. 過期邏輯」Lease#isisExpired(additionalLeaseMs)
方法 揭曉。#compensationTimeMs()
實現代碼如下:/*** 最後任務執行時間*/private final AtomicLong lastExecutionNanosRef = new AtomicLong(0L);long getCompensationTimeMs() {long currNanos = getCurrentTimeNano();long lastNanos = lastExecutionNanosRef.getAndSet(currNanos);if (lastNanos == 0L) {return 0L;}long elapsedMs = TimeUnit.NANOSECONDS.toMillis(currNanos - lastNanos);long compensationTime = elapsedMs - serverConfig.getEvictionIntervalTimerInMs();return compensationTime <= 0L ? 0L : compensationTime;}由於 JVM GC ,又或是時間偏移( clock skew ) 等原因,定時器執行實際比預期會略有延遲。筆者在本機低負載運行,大概 10 ms 內。
compute a compensation time defined as the actual time this task was executed since the prev iteration, vs the configured amount of time for execution. This is useful for cases where changes in time (due to clock skew or gc for example) causes the actual eviction task to execute later than the desired time according to the configured cycle.
調用
#evict(compensationTime)
方法,執行清理過期租約邏輯,在 「4. 過期邏輯」 詳細解析。
- 第 3 至 7 行 :判斷允許執行清理過期租約邏輯,主要和自我保護機制有關,在 《Eureka 源碼解析 —— 應用實例註冊發現(四)之自我保護機制》 有詳細解析。
第 9 至 24 行 :獲得所有過期的租約集合。
第 19 行 :調用
Lease#isisExpired(additionalLeaseMs)
方法,判斷租約是否過期,實現代碼如下:// Lease.javapublic boolean isExpired(long additionalLeaseMs) {return (evictionTimestamp > 0 || System.currentTimeMillis() > (lastUpdateTimestamp + duration + additionalLeaseMs));}public void renew() {lastUpdateTimestamp = System.currentTimeMillis() + duration;}��注意:在不考慮
additionalLeaseMs
參數的情況下,租約過期時間比預期多了一個duration
,原因在於#renew()
方法錯誤的設置lastUpdateTimestamp = System.currentTimeMillis() + duration
,正確的設置應該是lastUpdateTimestamp = System.currentTimeMillis()
。Note that due to renew() doing the ‘wrong” thing and setting lastUpdateTimestamp to +duration more than what it should be, the expiry will actually be 2 duration. *This is a minor bug and should only affect instances that ungracefully shutdown. Due to possible wide ranging impact to existing usage, this will not be fixed.
TODO[0023]:additionalLeaseMs
第 26 至 34 行 :計算最大允許清理租約的數量,後計算允許清理租約的數量。
��注意:即使 Eureka-Server 關閉自我保護機制,如果使用
renewalPercentThreshold = 0.85
默認配置,結果會是分批逐步過期。舉個例子:// 假設 20 個租約,其中有 10 個租約過期。// 第一輪執行開始int registrySize = 20;int registrySizeThreshold = (int) (20 * 0.85) = 17;int evictionLimit = 20 - 17 = 3;int toEvict = Math.min(10, 3) = 3;// 第一輪執行結束,剩餘 17 個租約,其中有 7 個租約過期。// 第二輪執行開始int registrySize = 17;int registrySizeThreshold = (int) (17 * 0.85) = 14;int evictionLimit = 17 - 14 = 3;int toEvict = Math.min(7, 3) = 3;// 第二輪執行結束,剩餘 14 個租約,其中有 4 個租約過期。// 第三輪執行開始int registrySize = 14;int registrySizeThreshold = (int) (14 * 0.85) = 11;int evictionLimit = 14 - 11 = 3;int toEvict = Math.min(4, 3) = 3;// 第三輪執行結束,剩餘 11 個租約,其中有 1 個租約過期。// 第四輪執行開始int registrySize = 11;int registrySizeThreshold = (int) (11 * 0.85) = 9;int evictionLimit = 11 - 9 = 2;int toEvict = Math.min(1, 2) = 1;// 第四輪執行結束,剩餘 10 個租約,其中有 0 個租約過期。結束。- 結論:是否開啓自我保護的差別,在於是否執行清理過期租約邏輯。如果想關閉分批逐步過期,設置
renewalPercentThreshold = 0
。
- 結論:是否開啓自我保護的差別,在於是否執行清理過期租約邏輯。如果想關閉分批逐步過期,設置
由於 JVM GC ,或是本地時間差異原因,可能自我保護機制的閥值
expectedNumberOfRenewsPerMin
、numberOfRenewsPerMinThreshold
不夠正確,在過期這個相對“危險”的操作,重新計算自我保護的閥值。
第 35 至 51 行 :隨機清理過期的租約。由於租約是按照應用順序添加到數組,通過隨機的方式,儘量避免單個應用被全部過期。
- 第 39 行 :傳入當前時間爲種子生成隨機,避免 Java 的僞隨機情況。在 《爲什麼說Java中的隨機數都是僞隨機數?》 有詳細解析。
- 第 41 至 43 行 :隨機調換後面的元素到當前位置(
i
)。
- 第 50 行 :調用
#internalCancel()
方法,下線已過期的租約,在 《Eureka 源碼解析 —— 應用實例註冊發現(四)之自我保護機制》「3.2 下線應用實例信息」 有詳細解析。
1. 概述
本文主要分享 Eureka-Server 過期超時續租的租約。
推薦 Spring Cloud 書籍:
2. 爲什麼需要過期
正常情況下,應用實例下線時候會主動向 Eureka-Server 發起下線請求。但實際情況下,應用實例可能異常崩潰,又或者是網絡異常等原因,導致下線請求無法被成功提交。
介於這種情況,通過 Eureka-Client 心跳延長租約,配合 Eureka-Server 清理超時的租約解決上述異常。
3. EvictionTask
com.netflix.eureka.registry.AbstractInstanceRegistry.EvictionTask
,清理租約過期任務。在 Eureka-Server 啓動時,初始化 EvictionTask 定時執行,實現代碼如下:
|
4. 過期邏輯
調用 #evict(compensationTime)
方法,執行清理過期租約邏輯,實現代碼如下:
|