1. 程式人生 > >Eureka服務發現的常見問題(使用的注意事項)

Eureka服務發現的常見問題(使用的注意事項)

寫這篇部落格的主要目的是解釋一些使用Eureka可能出現的問題或者不容易弄清的概念,會對以下問題加以說明:

  1. Server節點能否在配置檔案裡配置自身的Server地址
  2. Server是否配置registerWithEureka和fetchRegistry有什麼區別
  3. Server和Client節點配置全部的Server地址和部分Server地址有什麼區別
  4. Server回收服務資訊的自我保護機制是什麼?需要注意什麼
  5. Server節點間的服務資訊同步的流程是怎麼樣的

我們就以下圖為例來解釋上述問題:

Eureka服務發現的流程圖

上圖已經很直接的展示了Server和Server之間,Server和Client之間可能的連線情況,他們之間的連線情況就代表著配置檔案中的服務配置。

第一個問題:Server節點能否在配置檔案裡配置自身的服務發現地址?

可以的!當一個Server解析配置的叢集地址時,會過濾掉自身的地址,這樣服務同步時就不需要同步自身了。我們配置多個Server時,不需要手動的排除Server自身的發現地址。

就像上圖的Server 0,1,2的配置,每個節點可以都加上他們3個的服務發現地址,但他們在實際初始化時,每個Server裡只會生成2個用於資料同步的內建Node,比如 Server 0 初始化時生成1和2的資料同步Node,類名叫 PeerEurekaNode。

第二個問題:Server是否配置registerWithEureka和fetchRegistry有什麼區別?

其實每一個Eureka Server Node都內建了一個Eureka Client,也就是說一個Server Node節點可以接受其他Client的註冊,也可以作為一個Client註冊到其他Server上,被其他Client發現和呼叫。Server Node上的Server和Client可以理解為兩個容器,他們僅僅在初始化時打交道,之後就沒有什麼關聯了。

registerWithEurekafetchRegistry的預設值都是true,他們都是客戶端配置,也就是eureka.client開頭的配置資訊。

通過上述說明就很好理解這兩個引數設定與否的區別了:

  • registerWithEureka
    :是否要註冊到其他Server上。如果我的Server上其實開放了一些Http介面供呼叫,那麼就需要註冊,這樣其他的Client才能發現我的服務,才能通過RPC呼叫我提供的Http介面。如果我的Server沒有提供對外Http介面,那麼這個引數可以設定為false。
  • fetchRegistry:是否需要拉取服務資訊。和是否註冊一樣,如果我的Server需要呼叫其他的Client的Http介面,那麼就需要獲取相應的服務發現資訊,這樣才能正常的呼叫。同時這個引數還有一個重要的作用,就是決定Server在初始化時是否立即全量同步其他節點的服務資訊!!!Server初始化時會先初始化其內建的Client。若配置了fetchRegistry=true,那麼Client在初始化時會從其他Server全量拉取服務資訊,放進Client容器中。Server在初始化時會嘗試同步Client容器裡的服務資訊,如果fetchRegistry=false,服務資訊不存在,只能被動的等其他Server節點以增量的形式同步過來(Client在執行註冊和心跳時對應的註冊Server節點會廣播此事件,同步給其他的Server節點。當其他Server節點還沒有此服務資訊時,改為註冊此服務資訊)。當然正常的通過心跳來同步,最多也僅需要30S而已,是否需要設定此引數就看各自的需求了。

第三個問題:Server和Client節點配置全部的Server地址和部分Server地址有什麼區別?

這個問題主要的牽扯到了Server節點間的同步機制。

Client在與Server互動時,只會與其中的一個Server進行互動

Server之間的資料同步和Server與Client間的資料互動使用的是同一個Http介面,比如註冊,心跳,狀態更新,關閉服務等等。只是Server與Server之間同步時,會有一個Header引數,x-netflix-discovery-replication = true ,Server通過這個標識來判斷當前請求是來自Server還是來自Client。如果 x-netflix-discovery-replication不存在,則指明請求來自Client,Server在處理此請求時還會將請求廣播給配置上的其他Server節點,在廣播請求時,Header帶上x-netflix-discovery-replication=true。當其他Server節點接受到此請求時,通過此Header引數判斷是一個Server同步請求,因此只處理此請求,而不再廣播。Server之間的資料同步只傳播一次!!!

如上圖所示,根據以上原則:

  • ClientD如果選擇和Server4互動,那麼Server4將服務資訊同步給Server0,ClientA能發現ClientD;
  • ClientD如果選擇和Server1互動,那麼Server4將服務資訊同步給Server0,2,ClientA,B都能發現ClientD;
  • ClientC無論如何也不能發現ClientD
  • ClientC因為只和Server3互動,當Server3宕機時,其就失去了與Server叢集的聯絡,但因為其還儲存有ClientA的服務資訊,所以不影響其與ClientA的呼叫 ( 前提是ClientA正常與Server2互動,如果其一直與Server0互動,那麼Server2不會傳播ClientA的服務資訊 )。但ClientA發生的變化其得不到通知,而且後續新增的其他服務也發現不了。
  • 在Server3宕機時,Server2當時還儲存著ClientC的服務資訊,在那個時間,其他服務還是能通過Server2發現ClientC的,但是因為ClientC的不能續約,在90S後Server2將刪除ClientC的服務資訊。ClientC與服務叢集基本隔離。

第四個問題:Server回收服務資訊的自我保護機制是什麼?需要注意什麼?

Server每隔60S執行一次服務資訊回收,移除那些心跳時間超時的。能夠回收有3個前提:

  1. 心跳資訊超時,也就是回收時間距離上次心跳時間超過90S。
  2. 開啟了租約過期功能,預設是開啟的。
  3. 未觸發自我保護機制。所謂的自我保護機制,指的是上一分鐘內,服務實際傳送心跳的總數超過預計總數的85%,可能近似理解為正常存活的Client超過85%。

我在上一篇關於Server端的定時任務裡對自我保護機制有較詳細的說明,這裡就不再複述了。

那需要注意些什麼?如果你的Client個數較少,比如就5個,或者說同一個Server對應的Client就5個,那麼當其中的一個宕機了,1/5=20%,直接就觸發了自我保護機制,宕機的服務資訊會一直存在,不會被回收。對於這種情況,在學習的情況下,可以關閉自我保護機制:eureka.server.enable-self-preservation = false;後者也可以設定Server觸發自我保護機制的臨界值,eureka.server.renewal-percent-threshold = 0.85,預設是85%,可以修改成適當的值,比如0.5。

第五個問題:Server節點間的服務資訊同步的流程是怎麼樣的?

Server在初始化時,會根據配置資訊生成與其他的Server同步的客戶端。每當Server接收到Client的服務請求時,會先處理請求,然後將自身作為一個Client的角色,用相同的請求資訊去請求配置裡的那些Server節點。會將同步請求封裝成一個Task,然後存入一個Queue中,Server定時的提取Queue裡的任務,批量的處理它們。

也就是說Server之間的服務同步是非同步執行的,而不像Zookeeper一樣,每個操作都需要過半數的節點執行成功後才返回給Client。同時Server之間的同步只會傳播一次,它們通過Header裡的一個引數來表名是來自Client的請求還是Server的請求。如果是Server的請求,那麼接收到此請求後不會再進行傳播。就好像上圖中的Server 0接收到Client A的服務請求,然後將此請求傳播給Server 1,2,4。這3個Server接收到請求後,通過Header引數判斷是Server間的資料同步,就不會再講此請求傳播給他們各自配置上的節點,比如Server 2不會傳播給Server 3,也就時Client A的服務新不可能傳播到Server 3上,Client C不可能發現Client A。