Eureka Server 在運行期間會去統計心跳失敗比例在 15 分鐘之內是否低於 85%,如果低於 85%,Eureka Server 會將這些實例保護起來,讓這些實例不會過期,但是在保護期內如果服務剛好這個服務提供者非正常下線了,此時服務消費者就會拿到一個無效的服務實例,此時會調用失敗,對於這 ...
Eureka Server 在運行期間會去統計心跳失敗比例在 15 分鐘之內是否低於 85%,如果低於 85%,Eureka Server 會將這些實例保護起來,讓這些實例不會過期,但是在保護期內如果服務剛好這個服務提供者非正常下線了,此時服務消費者就會拿到一個無效的服務實例,此時會調用失敗,對於這個問題需要服務消費者端要有一些容錯機制,如重試,斷路器等。
我們在單機測試的時候很容易滿足心跳失敗比例在 15 分鐘之內低於 85%,這個時候就會觸發 Eureka 的保護機制,一旦開啟了保護機制,則服務註冊中心維護的服務實例就不是那麼準確了,此時我們可以使用eureka.server.enable-self-preservation=false
來關閉保護機制,這樣可以確保註冊中心中不可用的實例被及時的剔除(不推薦)。
自我保護模式被激活的條件是:在 1 分鐘後,Renews (last min) < Renews threshold
。
這兩個參數的意思:
Renews threshold
:Eureka Server 期望每分鐘收到客戶端實例續約的總數。Renews (last min)
:Eureka Server 最後 1 分鐘收到客戶端實例續約的總數。
具體的值,我們可以在 Eureka Server 界面可以看到:
可以看到,我們部署了 3 個 Eureka Server(自註冊模式),另外,又部署 7 個服務,註冊到 Eureka Server 集群,參數值分別為:
Renews threshold
:17Renews (last min)
:20
下麵說下Renews threshold
和Renews threshold
具體計算方式。
Renews threshold 計算代碼:
this.expectedNumberOfRenewsPerMin = count * 2;
this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());
count
表示服務的數量,如果 Eureka Server 開啟自註冊模式,也算一個服務,比如我們上面的示例,count
的值就是 10(3 個自註冊服務 + 7 個獨立服務),serverConfig.getRenewalPercentThreshold()
預設是 0.85(可以通過eureka.server.renewal-percent-threshold
配置)。
所以,根據上面的分析,我們可以計算出Renews threshold
的值:(int)(10 * 2 * 0.85) = (int)17 = 17
。
Renews (last min)
計算方式:count * 2
,數值 2 表示每 30 秒 1 個心跳,每分鐘 2 個心跳的固定頻率因數,所以具體值為:10 * 2 = 20
。
如果在 1 分鐘後,Renews (last min) < Renews threshold
,預設需等待 5 分鐘(可以通過eureka.server.wait-time-in-ms-when-sync-empty
配置),即 5 分鐘後你會看到下麵的提示信息:
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.
解決方式有三種:
- 關閉自我保護模式(
eureka.server.enable-self-preservation
設為false
),不推薦。 - 降低
renewalPercentThreshold
的比例(eureka.server.renewal-percent-threshold
設置為0.5
以下,比如0.49
),不推薦。 - 部署多個 Eureka Server 並開啟其客戶端行為(
eureka.client.register-with-eureka
不要設為false
,預設為true
),推薦。
Eureka 的自我保護模式是有意義的,該模式被激活後,它不會從註冊列表中剔除因長時間沒收到心跳導致租期過期的服務,而是等待修複,直到心跳恢復正常之後,它自動退出自我保護模式。這種模式旨在避免因網路分區故障導致服務不可用的問題。例如,兩個客戶端實例 C1 和 C2 的連通性是良好的,但是由於網路故障,C2 未能及時向 Eureka 發送心跳續約,這時候 Eureka 不能簡單的將 C2 從註冊表中剔除。因為如果剔除了,C1 就無法從 Eureka 伺服器中獲取 C2 註冊的服務,但是這時候 C2 服務是可用的。
所以,Eureka 的自我保護模式最好還是開啟它。
參考資料:
- Spring Cloud Eureka — 服務發現
- Spring cloud 系列五 Eureka 之集群同步、自我保護模式以及實例註冊、心跳、下線
- Spring Cloud Eureka 自我保護機制