分佈式理論學習總結

分佈式理論

CAP

CAP定理講的是三個性。consistency數據一致性,availability可用性,partition tolerance分區容錯性。node

三者只能選其中二者。爲何呢,看看這三個性質意味着什麼吧。git

首先看看分區容錯性,分區容錯性指的是網絡出現分區(丟包,斷網,超時等狀況都屬於網絡分區)時,整個服務仍然可用。github

因爲網絡分區在實際環境下必定存在,因此必須首先被考慮。因而分區容錯性是必需要保證的,不然一旦出現分區服務就不可用,那就沒辦法弄了。算法

因此其實是2選1的問題。在可用性和一致性中作出選擇。數據庫

在一個分佈式環境下,多個節點一塊兒對外提供服務,若是要保證可用性,那麼一臺機器宕機了仍然有其餘機器能提供服務。 可是宕機的機器重啓之後就會發現數據和其餘機器存在不一致,那麼一致性就沒法獲得保證。緩存

若是保證一致性,若是有機器宕機,那麼其餘節點就不能工做了,不然必定會產生數據不一致。服務器

BASE

在這麼嚴苛的規定下,CAP通常很難實現一個健壯的系統。因而提出了BASE來削弱這些要求。網絡

BASE是基本可用basically available,soft state軟狀態,eventually consistent最終一致性。session

基本可用就是容許服務在某些時候降級,好比淘寶在高峯時期會關閉退貨等服務。分佈式

軟狀態就是容許數據出現中間狀態,好比支付寶提交轉帳之後並非馬上到帳,中間通過了屢次消息傳遞和轉發。

最終一致性就是指數據最終要是一致的,好比多個節點的數據須要按期同步,支付寶轉帳最終必定會到帳。

分佈式系統關鍵詞

時鐘,時間,事件順序

分佈式系統的一個問題在與缺乏全局時鐘,因此你們沒有一個統一的時間,就很難用時間去肯定各個節點事件的發生順序,爲了保證事件的順序執行,

Lamport timestamps

Leslie Lamport 在1978年提出邏輯時鐘的概念,並描述了一種邏輯時鐘的表示方法,這個方法被稱爲Lamport時間戳(Lamport timestamps)[3]。

image

分佈式系統中按是否存在節點交互可分爲三類事件,一類發生於節點內部,二是發送事件,三是接收事件。Lamport時間戳原理以下:

每一個事件對應一個Lamport時間戳,初始值爲0
若是事件在節點內發生,時間戳加1
若是事件屬於發送事件,時間戳加1並在消息中帶上該時間戳
若是事件屬於接收事件,時間戳 = Max(本地時間戳,消息中的時間戳) + 1
複製代碼

這樣的話,節點內的事件有序,發送事件有序,接收事件必定在發送事件之後發生。再加上人爲的一些規定,所以根據時間戳能夠肯定一個全序排列。

Vector clock

Lamport時間戳幫助咱們獲得事件順序關係,但還有一種順序關係不能用Lamport時間戳很好地表示出來,那就是同時發生關係(concurrent)[4]。 Vector clock是在Lamport時間戳基礎上演進的另外一種邏輯時鐘方法,它經過vector結構不但記錄本節點的Lamport時間戳,同時也記錄了其餘節點的Lamport時間戳[5][6]。

image

若是 Tb[Q] > Ta[Q] 而且 Tb[P] < Ta[P],則認爲a、b同時發生,記做 a <-> b。例如圖2中節點B上的第4個事件 (A:2,B:4,C:1) 與節點C上的第2個事件 (B:3,C:2) 沒有因果關係、屬於同時發生事件。

由於B4 > B3而且 C1<C2,說明二者之間沒有順序關係,不然不會出現一大一小,所以他們是同時發生的。

Version vector

基於Vector clock咱們能夠得到任意兩個事件的順序關係,結果或爲前後順序或爲同時發生,識別事件順序在工程實踐中有很重要的引伸應用,最多見的應用是發現數據衝突(detect conflict)。

分佈式系統中數據通常存在多個副本(replication),多個副本可能被同時更新,這會引發副本間數據不一致[7],Version vector的實現與Vector clock很是相似[8],目的用於發現數據衝突[9]。

當兩個寫入數據事件同時發生則發生了衝突,因而經過某些方法解決數據衝突。

Vector clock只用於發現數據衝突,不能解決數據衝突。如何解決數據衝突因場景而異,具體方法有以最後更新爲準(last write win),或將衝突的數據交給client由client端決定如何處理,或經過quorum決議事先避免數據衝突的狀況發生[11]。

選主,租約,多數派

選舉(election)是分佈式系統實踐中常見的問題,經過打破節點間的對等關係,選得的leader(或叫master、coordinator)有助於實現事務原子性、提高決議效率。

多數派(quorum)的思路幫助咱們在網絡分化的狀況下達成決議一致性,在leader選舉的場景下幫助咱們選出惟一leader。

租約(lease)在必定期限內給予節點特定權利,也能夠用於實現leader選舉。

選舉(electioin)

一致性問題(consistency)是獨立的節點間如何達成決議的問題,選出你們都承認的leader本質上也是一致性問題,於是如何應對宕機恢復、網絡分化等在leader選舉中也須要考量。

在一致性算法Paxos、ZAB[2]、Raft[3]中,爲提高決議效率均有節點充當leader的角色。

ZAB、Raft中描述了具體的leader選舉實現,與Bully算法相似ZAB中使用zxid標識節點,具備最大zxid的節點表示其所具有的事務(transaction)最新、被選爲leader。

多數派(quorum)

在網絡分化的場景下以上Bully算法會遇到一個問題,被分隔的節點都認爲本身具備最大的序號、將產生多個leader,這時候就須要引入多數派(quorum)[4]。多數派的思路在分佈式系統中很常見,其確保網絡分化狀況下決議惟一。

租約(lease)

選舉中很重要的一個問題,以上還沒有提到:怎麼判斷leader不可用、何時應該發起從新選舉?

最早可能想到會經過心跳(heart beat)判別leader狀態是否正常,但在網絡擁塞或瞬斷的狀況下,這容易致使出現雙主。

租約(lease)是解決該問題的經常使用方法,其最初提出時用於解決分佈式緩存一致性問題[6],後面在分佈式鎖[7]等不少方面都有應用。

(a). 節點0、一、2在Z上註冊本身,Z根據必定的規則(例如先到先得)頒發租約給節點,該租約同時對應一個有效時長;這裏假設節點0得到租約、成爲leader

(b). leader宕機時,只有租約到期(timeout)後才從新發起選舉,這裏節點1得到租約、成爲leader
複製代碼

租約機制確保了一個時刻最多隻有一個leader,避免只使用心跳機制產生雙主的問題。在實踐應用中,zookeeper、ectd可用於租約頒發。

一致性,2pc和3pc

一致性(consensus)

何爲一致性問題?簡單而言,一致性問題就是相互獨立的節點之間如何達成一項決議的問題。分佈式系統中,進行數據庫事務提交(commit transaction)、Leader選舉、序列號生成等都會遇到一致性問題。

爲了保證執行的一致性,可使用2pc兩段式提交和3pc三段式提交。

2PC

2PC(tow phase commit)兩階段提交[5]顧名思義它分紅兩個階段,先由一方進行提議(propose)並收集其餘節點的反饋(vote),再根據反饋決定提交(commit)或停止(abort)事務。咱們將提議的節點稱爲協調者(coordinator),其餘參與決議節點稱爲參與者(participants, 或cohorts):

舉個例子,首先用戶想要執行一個事務,因而提交給leader,leader先讓各個節點執行該事務。

咱們要知道,事務是經過日誌來實現的。各個節點使用redo日誌進行重作,使用undo日誌進行回滾。

因而各個節點執行事務,並把執行結果是否成功返回給leader,當leader收到所有確認消息後,發送消息讓全部節點commit。若是有節點執行失敗,則leader要求全部節點回滾。

2pc可能出現的一些問題是:

1 leader必須等待全部節點結果,若是有節點宕機或超時,則拒絕該事務,並向節點發送回滾的信息。

2 若是leader宕機,則通常配置watcherdog自動切換成備用leader,而後進行下一次的請求提交。

3這兩種狀況單獨發生時都沒有關係,有對應的措施能夠進行回滾,可是若是當一個節點宕機時leader正在等待全部節點消息,其餘節點也在等待leader最後的消息。

此時leader也不幸宕機,切換以後leader並不知道一個節點宕機了,這樣的話其餘的節點也會被阻塞住致使沒法回滾。

3PC

image
coordinator接收完participant的反饋(vote)以後,進入階段2,給各個participant發送準備提交(prepare to commit)指令

。participant接到準備提交指令後能夠鎖資源,但要求相關操做必須可回滾。coordinator接收完確認(ACK)後進入階段三、進行commit/abort,3PC的階段3與2PC的階段2無異。協調者備份(coordinator watchdog)、狀態記錄(logging)一樣應用在3PC。

participant若是在不一樣階段宕機,咱們來看看3PC如何應對:

階段1: coordinator或watchdog未收到宕機participant的vote,直接停止事務;宕機的participant恢復後,讀取logging發現未發出同意vote,自行停止該次事務

階段2: coordinator未收到宕機participant的precommit ACK,但由於以前已經收到了宕機participant的同意反饋(否則也不會進入到階段2),coordinator進行commit;watchdog能夠經過問詢其餘participant得到這些信息,過程同理;宕機的participant恢復後發現收到precommit或已經發出同意vote,則自行commit該次事務

階段3: 即使coordinator或watchdog未收到宕機participant的commit ACK,也結束該次事務;宕機的participant恢復後發現收到commit或者precommit,也將自行commit該次事務 由於有了準備提交(prepare to commit)階段,3PC的事務處理延時也增長了1個RTT,變爲3個RTT(propose+precommit+commit),可是它防止participant宕機後整個系統進入阻塞態,加強了系統的可用性,對一些現實業務場景是很是值得的。

總結一下就是:階段一leader要求節點準備,節點返回ack或者fail。

若是節點都是ack,leader返回ack進入階段二。 (若是fail則回滾,由於節點沒有接收到ack,因此最終都會回滾)

階段二時節點執行事務而且發送結果給leader,leader返回ack或者fail。因爲階段二的節點已經有了一個肯定的狀態ack,若是leader超時或宕機不返回,成功執行節點也會進行commit操做,這樣即便有節點宕機也不會影響到其餘節點。

一致性算法paxos

Basic Paxos

何爲一致性問題?簡單而言,一致性問題是在節點宕機、消息無序等場景可能出現的狀況下,相互獨立的節點之間如何達成決議的問題,做爲解決一致性問題的協議,Paxos的核心是節點間如何肯定並只肯定一個值(value)。

和2PC相似,Paxos先把節點分紅兩類,發起提議(proposal)的一方爲proposer,參與決議的一方爲acceptor。假如只有一個proposer發起提議,而且節點不宕機、消息不丟包,那麼acceptor作到如下這點就能夠肯定一個值。

proposer發出提議,acceptor根據提議的id和值來決定是否接收提議,接受提議則替換爲本身的提議,而且返回以前id最大的提議,當超過一半節點提議該值時,則該值被肯定,這樣既保證了時序,也保證了多數派。

Multi Paxos

經過以上步驟分佈式系統已經能肯定一個值,「只肯定一個值有什麼用?這可解決不了我面臨的問題。」 你心中可能有這樣的疑問。

其實不斷地進行「肯定一個值」的過程、再爲每一個過程編上序號,就能獲得具備全序關係(total order)的系列值,進而能應用在數據庫副本存儲等不少場景。咱們把單次「肯定一個值」的過程稱爲實例(instance),它由proposer/acceptor/learner組成。

Fast Paxos

在Multi Paxos中,proposer -> leader -> acceptor -> learner,從提議到完成決議共通過3次通訊,能不能減小通訊步驟?

對Multi Paxos phase2a,若是能夠自由提議value,則可讓proposer直接發起提議、leader退出通訊過程,變爲proposer -> acceptor -> learner,這就是Fast Paxos[2]的由來。

屢次paxos的肯定值使用可讓多個proposer,acceptor一塊兒運做。多個proposer提出提議,acceptor保留最大提議比返回以前提議,proposer當提議數量知足多數派則取出最大值向acceptor提議,因而過半數的acceptor比較提議後能夠接受該提議,因而最終leader將提議寫入acceptor,而acceptor再寫入對應的learner。

raft和zab

Zab

Zab[5][6]的全稱是Zookeeper atomic broadcast protocol,是Zookeeper內部用到的一致性協議。相比Paxos,Zab最大的特色是保證強一致性(strong consistency,或叫線性一致性linearizable consistency)。

和Raft同樣,Zab要求惟一Leader參與決議,Zab能夠分解成discovery、sync、broadcast三個階段:

image

discovery: 選舉產生PL(prospective leader),PL收集Follower epoch(cepoch),根據Follower的反饋PL產生newepoch(每次選舉產生新Leader的同時產生新epoch,相似Raft的term)

sync: PL補齊相比Follower多數派缺失的狀態、以後各Follower再補齊相比PL缺失的狀態,PL和Follower完成狀態同步後PL變爲正式Leader(established leader)

broadcast: Leader處理Client的寫操做,並將狀態變動廣播至Follower,Follower多數派經過以後Leader發起將狀態變動落地(deliver/commit)

Raft:

單個 Candidate 的競選

有三種節點:Follower、Candidate 和 Leader。Leader 會週期性的發送心跳包給 Follower。每一個 Follower 都設置了一個隨機的競選超時時間,通常爲 150ms~300ms,若是在這個時間內沒有收到 Leader 的心跳包,就會變成 Candidate,進入競選階段。

  • 下圖表示一個分佈式系統的最初階段,此時只有 Follower,沒有 Leader。Follower A 等待一個隨機的競選超時時間以後,沒收到 Leader 發來的心跳包,所以進入競選階段。

  • 此時 A 發送投票請求給其它全部節點。

  • 其它節點會對請求進行回覆,若是超過一半的節點回復了,那麼該 Candidate 就會變成 Leader。

  • 以後 Leader 會週期性地發送心跳包給 Follower,Follower 接收到心跳包,會從新開始計時。

多個 Candidate 競選

  • 若是有多個 Follower 成爲 Candidate,而且所得到票數相同,那麼就須要從新開始投票,例以下圖中 Candidate B 和 Candidate D 都得到兩票,所以須要從新開始投票。

  • 當從新開始投票時,因爲每一個節點設置的隨機競選超時時間不一樣,所以能下一次再次出現多個 Candidate 並得到一樣票數的機率很低。

日誌複製

  • 來自客戶端的修改都會被傳入 Leader。注意該修改還未被提交,只是寫入日誌中。

  • Leader 會把修改複製到全部 Follower。

  • Leader 會等待大多數的 Follower 也進行了修改,而後纔將修改提交。

  • 此時 Leader 會通知的全部 Follower 讓它們也提交修改,此時全部節點的值達成一致。

zookeeper

zookeeper在分佈式系統中做爲協調員的角色,可應用於Leader選舉、分佈式鎖、配置管理等服務的實現。如下咱們從zookeeper供的API、應用場景和監控三方面學習和了解zookeeper(如下簡稱ZK)。

ZK API

ZK以Unix文件系統樹結構的形式管理存儲的數據,圖示以下:

其中每一個樹節點被稱爲znode,每一個znode相似一個文件,包含文件元信息(meta data)和數據。

如下咱們用server表示ZK服務的提供方,client表示ZK服務的使用方,當client鏈接ZK時,相應建立session會話信息。

有兩種類型的znode:

Regular: 該類型znode只能由client端顯式建立或刪除

Ephemeral: client端可建立或刪除該類型znode;當session終止時,ZK亦會刪除該類型znode

znode建立時還能夠被打上sequential標誌,被打上該標誌的znode,將自行加上自增的數字後綴

ZK提供瞭如下API,供client操做znode和znode中存儲的數據:

create(path, data, flags):建立路徑爲path的znode,在其中存儲data[]數據,flags可設置爲Regular或Ephemeral,並可選打上sequential標誌。

delete(path, version):刪除相應path/version的znode

exists(path,watch):若是存在path對應znode,則返回true;不然返回false,watch標誌可設置監聽事件

getData(path, watch):返回對應znode的數據和元信息(如version等)

setData(path, data, version):將data[]數據寫入對應path/version的znode

getChildren(path, watch):返回指定znode的子節點集合
複製代碼

K應用場景

基於以上ZK提供的znode和znode數據的操做,可輕鬆實現Leader選舉、分佈式鎖、配置管理等服務。

Leader選舉

利用打上sequential標誌的Ephemeral,咱們能夠實現Leader選舉。假設須要從三個client中選取Leader,實現過程以下:

一、各自建立Ephemeral類型的znode,並打上sequential標誌:

[zk: localhost:2181(CONNECTED) 4] ls /master [lock-0000000241, lock-0000000243, lock-0000000242]

二、檢查 /master 路徑下的全部znode,若是本身建立的znode序號最小,則認爲本身是Leader;不然記錄序號比本身次小的znode

三、非Leader在次小序號znode上設置監聽事件,並重復執行以上步驟2

配置管理

znode能夠存儲數據,基於這一點,咱們能夠用ZK實現分佈式系統的配置管理,假設有服務A,A擴容設備時須要將相應新增的ip/port同步到全網服務器的A.conf配置,實現過程以下:

一、A擴容時,相應在ZK上新增znode,該znode數據形式以下:

[zk: localhost:2181(CONNECTED) 30] get /A/blk-0000340369 {"svr_info": [{"ip": "1.1.1.1.", "port": "11000"}]} cZxid = 0x2ffdeda3be ……

二、全網機器監聽 /A,當該znode下有新節點加入時,調用相應處理函數,將服務A的新增ip/port加入A.conf

三、完成步驟2後,繼續設置對 /A監聽

ZK監控

ZK自身提供了一些「四字命令」,經過這些四字命令,咱們能夠得到ZK集羣中,某臺ZK的角色、znode數、健康狀態等信息:

小結

zookeeper以目錄樹的形式管理數據,提供znode監聽、數據設置等接口,基於這些接口,咱們能夠實現Leader選舉、配置管理、命名服務等功能。結合四字命令,加上模擬zookeeper client 建立/刪除znode,咱們能夠實現對zookeeper的有效監控。在各類分佈式系統中,咱們常常能夠看到zookeeper的身影。

相關文章
相關標籤/搜索