天天看點

Spring-Cloud 服務注冊與發現 EurekaCAP服務注冊與發現參考文獻

CAP

在分布式系統領域有個著名的CAP定理:C -- 資料一緻性(Consistency),A -- 服務可用性(Availability),P -- 分區容錯性(Partition tolerance)。

資料一緻性

一緻性指“all nodes see the same data at the same time”,即更新操作成功并傳回用戶端完成後,所有節點在同一時間的資料完全一緻。

服務可用性

可用性指“Reads and writes always succeed”,即服務一直可用,而且是正常響應時間。

分區容錯性

分區容錯性指“the system continues to operate despite arbitrary message loss or failure of part of the system”,即分布式系統在遇到某節點或網絡分區故障的時候,仍然能夠對外提供滿足一緻性和可用性的服務。

一個分布式系統最多隻能同時CAP這三項中的兩項。

服務注冊與發現

對于微服務的治理而言,核心就是服務的注冊和發現。這方面的開源架構很多,最出名的應該是Zookeeper,但這也許并不代表就是一個最佳選擇。

Zookeeper是著名Hadoop的一個子項目。Zookeeper保證的是CP,即選擇保證資料一緻性和分區容錯性,代價是它無法保證每次服務請求的可用性。從實際情況來分析,在使用Zookeeper擷取服務清單時,如果zookeeper正在選主,或者Zookeeper叢集中半數以上機器不可用,那麼将就無法獲得資料了。是以說,Zookeeper不能保證服務可用性。

對于設計資料存儲場景的分布式環境,的确應當把資料一緻性作為首要考慮的方向,這也是zookeeper設計成CP的原因。

但是對于服務發現場景來說,資料一緻性就不再是首要考慮的。對某個服務來說,即使注冊中心的每個節點所儲存的服務提供者資訊有所不同,也并不會造成災難性的後果。因為對于服務消費者來說,關注的是可以消費,哪怕說拿到了不正确的服務執行個體去消費,也比無法消費要好一些。是以,對于服務發現而言,可用性理應比資料一緻性更加值得首要考慮,即AP勝過CP。是以Spring Cloud Netflix在設計Eureka時遵守的就是AP原則。

Eureka

Spring-Cloud 服務注冊與發現 EurekaCAP服務注冊與發現參考文獻

從上圖可以看出,Eureka的組成執行個體分為如下兩種:Eureka Server和Eureka Client。

  • Eureka Server,服務的注冊中心,負責維護注冊的服務清單。
  • Service Provider,服務提供方,Eureka Client,向Eureka Server做服務注冊、續約和下線等操作,注冊的主要資料包括服務名、機器ip、端口号、域名等等。
  • Service Consumer,服務消費方,Eureka Client,向Eureka Server擷取Service Provider的注冊資訊,并通過遠端調用與Service Provider進行通信。

圖中的Service Provider(服務提供者)和Service Consumer(服務發現者)隻是使用上的角色差別而已,并不是嚴格的概念,在實際使用中,二者角色可以互換,設定可以同時兼有兩種角色的功能。

Spring Cloud針對服務注冊與發現,提供了三種實作:Eureka、Consul、Zookeeper。目前支援得最好的就是Eureka,其次是Consul,最後是Zookeeper。

Eureka Server

Eureka Server作為一個獨立的部署單元,以REST API的形式為服務執行個體提供了注冊、管理和查詢等操作。同時,Eureka Server也為我們提供了可視化的監控頁面,可以直覺地看到各個Eureka Server目前的運作狀态和所有已注冊服務的情況。

Eureka Server的高可用叢集

Eureka Server通過運作多個執行個體來建構叢集,解決單點問題,與ZooKeeper的leader/flower選舉機制不同,Eureka Server采用的是Peer to Peer對等通信。

這是一種去中心化的架構,并不存在主從之分,每一個Peer都是對等的。在這種模式下,各個節點之間通過互相注冊來提高可用性。每個節點都可被視為其他節點的副本。

如果某台Eureka Server當機,Eureka Client的請求會自動切換到新的Eureka Server節點,當當機的伺服器重新恢複後,Eureka會再次将其納入到伺服器叢集管理之中。當節點開始接受用戶端請求時,所有的操作都會在節點間進行複制操作,将請求複制到其他Eureka Server目前所知的所有節點中。

一個新的Eureka Server節點啟動後,會首先嘗試通過從鄰近節點擷取所有執行個體系統資料庫資訊,來完成自身的完成初始化。

Eureka Server通過getEurekaServiceUrls()方法擷取所有的節點,并且會通過心跳續約的方式定期更新。預設配置下,如果Eureka Server在一定時間内沒有接收到某個服務執行個體的心跳,Eureka Server将會登出該執行個體。當Eureka Server節點在短時間内丢失過多的心跳時(比如發生了網絡分區故障),那麼這個節點就會進入自我保護模式。下圖為Eureka官網的架構圖

Spring-Cloud 服務注冊與發現 EurekaCAP服務注冊與發現參考文獻

自我保護模式

預設配置下,如果Eureka Server每分鐘收到心跳續約的數量低于一個門檻值(instance的數量*(60/每個instance的心跳間隔秒數)*自我保護系數),并且持續15分鐘,就會觸發自我保護。在自我保護模式中,Eureka Server會保護服務系統資料庫中的資訊,不再登出任何服務執行個體。當它收到的心跳數重新恢複到門檻值以上時,該Eureka Server節點就會自動退出自我保護模式。它的設計思想就是甯可保留錯誤的服務注冊資訊,也不盲目登出任何可能健康的服務執行個體。

Service Provider

服務注冊

Service Provider本質上是一個Eureka Client。它啟動時,會調用服務注冊方法,向Eureka Server注冊自己的資訊。Eureka Server會維護一個已注冊服務的清單,這個清單為一個嵌套的hash map:

  • 第一層,application name和對應的服務執行個體。
  • 第二層,服務執行個體及其對應的注冊資訊,包括IP,端口号等。

當執行個體狀态發生變化時(如自身檢測認為Down的時候),也會向Eureka Server更新自己的服務狀态,同時通過節點間複制向其它Eureka Server節點做狀态同步。

Spring-Cloud 服務注冊與發現 EurekaCAP服務注冊與發現參考文獻

續約與剔除

前面提到過,服務執行個體啟動後,會周期性地向Eureka Server發送心跳以續約自己的資訊,避免自己的注冊資訊被剔除。

續約的方式與服務注冊基本一緻:首先更新自身狀态,再通過節點間複制同步到其它Peer。

如果Eureka Server在一段時間内沒有接收到某個微服務節點的心跳,Eureka Server将會登出該微服務節點(自我保護模式除外)。

Spring-Cloud 服務注冊與發現 EurekaCAP服務注冊與發現參考文獻

Service Consumer

Service Consumer本質上也是一個Eureka Client。它啟動後,會從Eureka Server上擷取所有執行個體的注冊資訊,包括IP位址、端口等,并緩存到本地(預設每30秒更新一次)。

前文提到過,如果與Eureka Server通信中斷,Service Consumer仍然可以通過本地緩存與Service Provider通信。實際開發Eureka的過程中,有時會遇見Service Consumer擷取到Server Provider的資訊有延遲,這是因為服務端的更改可能需要2分鐘才能傳播到所有用戶端(Eureka Wiki總并沒有給出原因),Eureka有三處緩存和一處延遲。

  • Eureka Server對注冊清單進行緩存,預設時間為30s。
  • Eureka Client對擷取到的注冊資訊進行緩存,預設時間為30s。
  • Ribbon會從上面提到的Eureka Client擷取服務清單,将負載均衡後的結果緩存30s。
  • 如果不是在Spring Cloud環境下使用這些元件(Eureka, Ribbon),服務啟動後并不會馬上向Eureka注冊,而是需要等到第一次發送心跳請求時才會注冊。心跳請求的發送間隔預設是30s。Spring Cloud對此做了修改,服務啟動後會馬上注冊。

參考文獻

  • https://github.com/Netflix/eureka/wiki
  • http://nobodyiam.com/2016/06/25/dive-into-eureka/

繼續閱讀