1. 程式人生 > >Spring Cloud系列教程 | 第四篇:Eurake的自我保護機制

Spring Cloud系列教程 | 第四篇:Eurake的自我保護機制

Eurake的自我保護機制

    從CAP定理角度看,Eureka是一個AP系統,以高可用性為主,而zookeeper則是CP,以高一致性為主,所以如果使用ZK在服務發現和註冊方面,可用服務資訊雖然很及時,但是會出現不可用情形,造成無法克服的生產事故。Eureka則是在出現網路分割槽期間(無法通訊,出現需要重試的情況就是由於網路已經分割槽了),登錄檔中的資訊在伺服器之間不一致,自我保護(self-preservation)功能旨在最大限度地減少這種不一致性。

定義自我保護

自我保護是一種功能,當Eureka伺服器超過某個時間閾值沒有收到心跳(來自同級和客戶端微服務)時,它會停止失效登錄檔中的例項,因為按一般常理,如果沒有收到某個例項的心跳,應該失效那個例項的心跳。

讓我們試著詳細瞭解這個概念。

從健康的系統開始

考慮以下健康系統。 在這裡插入圖片描述 假設所有微服務都處於健康狀態並在Eureka伺服器1上註冊。如果您想知為啥只註冊到server1原因 :因為註冊時配置導致,心跳只發送到service-url列表中配置的第一臺伺服器。即

eureka.client.service-url.defaultZone = server1,server2

Eureka伺服器與相鄰對等方之間者採取複製登錄檔資訊方式,並且這兩個伺服器登錄檔都指示所有微服務例項都處於UP狀態。上圖還說明例項2從Eureka登錄檔中發現例項4之後還呼叫了它。

遇到網路分割槽

假設發生網路分割槽,系統轉換到以下狀態。 在這裡插入圖片描述

上圖中由於網路分割槽例項4和5與伺服器失去連線,但是例項2仍然連線到例項4,這時Eureka伺服器會按照常理從登錄檔中刪除例項4和5,因為它不再接收到這兩個例項心跳,這時它也進入觀察期,如果在一段觀察期內又失去超過15%的心跳,它就會進入自我保護模式。

從現在開始,即使剩餘的例項也出現故障,Eureka伺服器也不會刪除登錄檔中的這些例項了。 在這裡插入圖片描述 上圖中例項3已關閉,但它在伺服器登錄檔中保持活動狀態。不過,伺服器還是能夠接受新的註冊例項。

自我保護背後的理由

注意:沒有接受心跳意味著兩種可能:網路故障;原例項真的下線了。如何判斷這兩個故障其實很難,這是分散式系統典型拜占庭將軍問題,解決方式一般兩個:接受現狀或重試。接受現狀就是允許網路故障的兩端資料不一致,根據CAP定理,就是選擇了AP,而如果不斷重試,則是為了讓兩端資料儘快一致起來,實際就是選擇 了CP,Dubbo的原始碼中使用Netty長連線以及ZoopKeeper本身的都是CA,這些都是符合人的自覺,但是Eureka選擇了接受現狀,選擇了AP,對網路分割槽的容忍性提高,可用性也提高了。

具體來說,由於以下兩個原因,自我保護功能可以證明是合理的。

  • 沒有接收心跳的伺服器可能是由於網路分割槽,接受分割槽,保留暫時的不一致,提高可用性。
  • 即使伺服器和某些客戶端之間的連線丟失,客戶端也可能相互連線。即,在網路分割槽期間,例項2還是具有與例項4的連線,如上圖所示。

自我保護的預設配置

下面列出的是可以直接或間接影響自我保護行為的配置。 eureka.instance.lease-renewal-interval-in-seconds = 30 表示客戶端向伺服器傳送心跳的頻率,不建議更改此值,因為自我保護假設始終以30秒的間隔接收心跳。

eureka.instance.lease-expiration-duration-in-seconds = 90 表示伺服器在收到最後一次心跳之後等待的持續時間,然後才能從其登錄檔中刪除例項。該值應大於lease-renewal-interval-in-seconds。此值設定得長會影響每分鐘實際心跳的精確性,在下一節中會有描述的,因為登錄檔的活力取決於這個值。將此值設定得太小可能會使系統無法容忍臨時網路故障。

eureka.server.eviction-interval-timer-in-ms = 60 * 1000 排程程式以此頻率執行,如果例項的租約按照這種配置時間發生過期現象,這將從登錄檔中刪除。將此值設定得太長會延遲系統進入自保護模式。

eureka.server.renewal-percent-threshold = 0.85 此值用於計算每分鐘的預期心跳。

eureka.server.renewal-threshold-update-interval-ms = 15 * 60 * 1000 排程程式以此頻率執行,計算每分鐘的預期心跳。

eureka.server.enable-self-preservation = true 最後但並非最不重要的是,如果需要,可以禁用自我保護。

理解配置

如果actual number of heartbeats in last minute小於expected number of heartbeats per minute,Eureka伺服器就會進入自我保護模式,

預期的每分鐘心跳次數

我們可以看到計算每分鐘心跳預期閾值的方法。Netflix程式碼假定此計算始終以30秒的間隔接收心跳。

假設某個時間點的已註冊應用程式例項數為N,配置renewal-percent-threshold為0.85。

  • 一個例項預期的心跳數/ 分鐘= 2
  • N個例項預期的心跳數/分鐘= 2 * N.
  • 預期最小心跳/ 分鐘 = 2 * N * 0.85

由於N是變數,因此預設情況下每15分鐘計算一次2 * N * 0.85結果。或基於renewal-threshold-update-interval-ms的設定時間計算。

最後一分鐘的實際心跳次數

這是由排程程式計算的,排程程式以一分鐘的頻率執行。

同樣如上所述,兩個排程器獨立執行以便計算實際和預期的心跳數。有另一個排程程式會比較這兩個值並確定系統是否處於自我儲存模式 - 也就是EvictionTask。這個排程程式以eviction-interval-timer-in-ms定義的時間頻率執行並刪除過期的例項,但它會在刪除之前檢查系統是否已達到自我保護模式(通過比較實際和預期的心跳)。

每次啟動時,eureka儀表板也會進行此比較,會顯示訊息“INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE”。

結論

  • 在大多數情況下,eureka會誤報微服務例項下線是因為糟糕的網路分割槽。
  • 自我保護永不失效,也就是一直保持著,直到並且除非關閉微服務(或解決網路故障)。
  • 如果啟用了自我保護,我們無法微調例項心跳間隔,因為自我保護假定心跳以30秒的間隔接收。
  • 除非你的環境中常有網路故障,否則將其關閉(即使大多數人建議將其保留),由於本地除錯很容易觸發保護機制,出現警告資訊: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引數來關閉保護機制。

下一篇: Spring Cloud系列教程 | 第五篇:使用Netflix Hystrix的Spring Cloud斷路器