CAP原則之ZK和Eureka注冊中心
分布式 CAP 原則與 BASE 理論
CAP
CAP 是 Consistency、Availablity、Partition-tolerance 的縮寫,由計算機科學家埃里克·布魯爾在 2000 年提出的,所以又稱布魯爾定理 (Brewer’s theorem),它指出對于一個分布式計算系統來說,不可能同時滿足以下三點
Consistency(一致性):如果對任意一個節點的數據就行修改成功后,所有其他節點都能讀取到最新的值,那么這個系統就被認為具有嚴格的一致性。
Availability(可用性):每次請求都能獲取到非錯的響應,即單節點宕機可從其他節點獲取到響應,但是不能保障獲取到的數據為最新的數據,即和一致性互斥
Partition tolerance(分區容錯性):當節點間出現網絡分區(不同節點處于不同的子網絡,子網絡之間是聯通的,但是子網絡之間是無法聯通的,也就是被切分成了孤立的集群網絡),照樣可以提供滿足一致性和可用性的服務,除非整個網絡環境都發生了故障。
任何一個分布式系統只能滿足三選二,即只能 AP 或 CP,必須要有 P 。
為什么 CAP 只能達到 CP 或者 AP?
CAP 認為分布式環境下網絡的故障是常態,比如我們多機房部署下機房間就可能發生光纜被挖斷、專線故障等網絡分區情況(導致部分節點無法通信,原本一個大集群變成多個獨立的小集群),也可能出現網絡波動、丟包、節點宕機等,所以分布式系統設計要考慮的是在滿足 P 的前提下選擇 C 還是 A。
拋開嚴謹的學術證明我們設想工作中的例子:我們要開發一個分布式緩存服務,只提供簡單的讀取與寫入功能,服務支持多個節點做數據冗余及負載,請求由網關隨機分發到其中一個節點,我們必須確保其中一個或幾個節點故障時另一些節點仍然可以提供服務,在網絡分區形成獨立小集群時也可以提供服務,這就必須滿足分區容錯性(P),我們假設部署了兩個服務節點,那么:
如果要保證一致性(C),即所有節點可查詢到的數據隨時隨刻都是一致的(同步中的數據不可查詢),就要求一個節點寫入數據后必須再將數據寫入到另一個節點后才能返回成功,這樣當我們讀取之前寫入的數據時才能確保一致,但上文說明過網絡異常在所難免,如果兩個服務節點無法相互通訊時為保證一致性在數據寫入發現無法同步到另一節點時就會返回錯誤進而犧牲了可用性(A)。
如果要保證可用性(A),即只要不是服務宕機所有請求都可得到正確的響應,那么在網絡異常節點不能通訊的情況下要讓數據沒有同步到另一節點的請求也返回成功,這就必須犧牲一致性(C)導致在一段時間內(網絡異常期間)兩個服務節點所查詢到的數據可能不同。
所以從中可以簡單地發現一致性(C)與可用性(A)是不可能同時滿足的。同 FLP Impossibility 一樣 CAP 理論也為我們做分布式服務架構指明了方向:分布式系統中我們只能選擇 CP(滿足一致性犧牲可用性)或 AP(滿足可用性犧牲一致性)。
當我們選擇 CP,即滿足一致性而犧牲可用性時意味著在網絡異常出現多個節點孤島時為了保證各個節點的數據一致系統會停止服務,反之選擇 AP,即滿足可用性犧牲一致性時網絡異常時系統仍可工作,但會出現各節點數據不致的情況。
在我們做微服務架構時需要知道 CAP 并做出架構設計或選型。比如注冊中心常用的 Eureka 和 Zookeepr 實現,Eureka 是 AP 的,Zookeeper 是 CP 的,Spring Cloud 之所以推薦 Eureka 是因為它認為注冊中心的場景允許出現短暫的數據不一致情況,可用性要高于強一致性,
上面出現了“強一致性”與“弱一致性”兩個概念,這其實是對一致性的延展,大量的工程實踐的經驗表明可用性很重要,一致性也很重要,但可以容許一定的時差,即只要保證在一定時間內達到一致即可,這也就是所謂的最終一致性。要實現強一致性的成本很高,尤其是存在很多數據副本的情況下,區塊鏈的 PoW 及其衍生算法就是典型的代表,它的共識機制是概率強一致性(Probabilistic Strong Consistency),要求等待大多數節點都接受了這筆交易再真正接受它,但是帶來的問題是交易的確認嚴重滯后。
基于此出現了 Base 理論。
BASE
BASE 是由 Basically Available(基本可用)、Soft state(軟狀態)、Eventually consistent(最終一致性)縮寫而來的。BASE 理論是對 CAP 中的一致性和可用性進行一個權衡的結果,理論的核心思想就是:我們無法做到強一致,但每個應用都可以根據自身的業務特點,采用適當的方式來使系統達到最終一致性,讓 CAP 三者同時基本實現。
Basically Available:基本可用,就是在某個節點宕機或者發生網絡分區的情況,可以讓所有請求都強制走主節點,這樣保證了數據的一致性可可用性,如果主節點壓力比較大可以觸發降級熔斷機制等,或者限流等,讓原先 0.5 秒響應的請求以更長的時間去相應
Soft state:軟狀態相對原子性來說各個要求都有所降低,原子性(硬狀態),要求多個節點的數據副本都是一致的,這是一種"硬狀態"。軟狀態(弱狀態)允許系統中的數據存在中間狀態,并認為該狀態不影響系統的整體可用性,即允許系統在多個不同節點的數據副本存在數據延遲
Eventually consistent:最終一致性,一致性也分強一致性和弱一致性,而最終一致性屬于弱一致性,就是系統并不保證連續進程或者線程的訪問都會返回最新的更新過的值。系統在數據寫入成功之后,不承諾立即可以讀到最新寫入的值,也不會具體的承諾多久之后可以讀到。但會盡可能保證在某個時間級別(比如秒級別)之后,可以讓數據達到一致性狀態。
基于 zookeeper 實現注冊中心(CP)
CP 模式,保證一致性
zookeeper 集群
zookeeper 集群是一主多從的模式
zookeeper 集群中的節點有三種角色
- Leader:處理集群的所有事務請求,集群中只有一個 Leader
- Follwoer:只能處理讀請求,參與 Leader 選舉
- Observer:只能處理讀請求,提升集群讀的性能,但不能參與 Leader 選舉
ZK 集群的數據同步機制
正常的客戶端數據提交流程(zookeeper 集群服務注冊訂閱)
步驟:
1、首先集群啟動時,會先進行領導者選舉,確定哪個節點是 Leader ,哪些節點是 Follower 和 Observer
2、然后 Leader 會和其他節點進行數據同步,采用發送快照和發送 Diff 日志的方式
3、集群在工作過程中,所有的寫請求都會交給 Leader 節點來進行處理,從節點只能處理讀請求
4、Leader 節點收到一個寫請求時,會通過兩階段機制來處理
5、Leader 節點會將該寫請求對應的日志發送給其他 Follower 節點,并等待 Follower 節點持久化日志成功
6、Follower 節點收到日志后會進行持久化,如果持久化成功則發送一個 Ack 給 Leader 節點
7、當 Leader 節點收到半數以上的 Ack 后,就會開始提交,先更新 Leader 節點本地的內存數據
8、然后發送 commit 命令給 Follower 節點, Follower 節點收到 commit 命令后就會更新各自本地內存數據
9、同時 Leader 節點還是將當前寫請求直接發送給 Observer 節點, Observer 節點收到 Leader 發過來的寫請求后直接執行更新本地內存數據
10、最后 Leader 節點返回客戶端請求響應成功
結論:通過同步機制和兩階段提交機制來達到集群中節點數據一致
節點宕機后的 Leader 選舉和數據同步流程
當 zookeeper 集群中的 Leader 宕機后,會觸發新的選舉,選舉期間,整個集群是沒法對外提供服務的。直到選出新的 Leader 之后,才能重新提供服務
選舉
步驟:
1、Leader 掛了,zookeeper 集群不可用
2、通過選舉,Follwoer1 成為了 Leader,zookeeper 集群可用
3、原來的 Leader 啟動起來了,變成了集群的 Follwoer5
4、Leader 通過 ZXID 事務 ID 向 Follwoer5 同步數據,Follwoer5 可用
結論:在 zookeeper 選舉和同步過程,zookeeper 集群不可用
不管是正常的客戶端數據提交流程還是節點宕機后的 Leader 選舉和數據同步流程都保證了 zookeeper 集群的一致性,但是在節點宕機后的 Leader 選舉和數據同步流程中 zookeeper 集群是不可用的,無法提供可用性,所以 zookeeper 保證了 AP,放棄了 C。
基于 Eureka 實現注冊中心(AP)
AP 模式保證可用性
Eureka 集群
eureka 集群中每個節點的角色都一樣,都可以提供事物請求和讀請求
eureka 服務注冊與發現
步驟:
1、Eureka Server 啟動成功,等待服務端注冊。在啟動過程中如果配置了集群,集群之間定時通過 Replicate 同步注冊表,每個 Eureka Server 都存在獨立完整的服務注冊表信息
2、Eureka Client 啟動時根據配置的 Eureka Server 地址去注冊中心注冊服務
3、Eureka Client 會每 30s 向 Eureka Server 發送一次心跳請求,證明客戶端服務正常
4、當 Eureka Server 90s 內沒有收到 Eureka Client 的心跳,注冊中心則認為該節點失效,會注銷該實例
5、單位時間內 Eureka Server 統計到有大量的 Eureka Client 沒有上送心跳,則認為可能為網絡異常,進入自我保護機制,不再剔除沒有上送心跳的客戶端
6、當 Eureka Client 心跳請求恢復正常之后,Eureka Server 自動退出自我保護模式
7、Eureka Client 定時全量或者增量從注冊中心獲取服務注冊表,并且將獲取到的信息緩存到本地
8、服務調用時,Eureka Client 會先從本地緩存找尋調取的服務。如果獲取不到,先從注冊中心刷新注冊表,再同步到本地緩存
9、Eureka Client 獲取到目標服務器信息,發起服務調用
10、Eureka Client 程序關閉時向 Eureka Server 發送取消請求,Eureka Server 將實例從注冊表中刪除
Eureka 集群每個節點都相等,都可以提供事物請求和讀請求,集群之間定時通過 Replicate 同步注冊表并通過心跳檢測機制去處理 Client 的上下線,保證了 CP,放棄了 A,這里放棄了一致性,只是說放棄了強一致性,去追求最終一致性
參考文獻
《全面解讀 CAP 定理》(https://github.com/Netflix/eureka)
《ZooKeeper:分布式過程協同技術詳解》(http://www.17bigdata.com/book/zookeeper/index.html)