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