Zab:Zookeeper 中的分布式一致性協(xié)議介紹

背景

在分布式系統(tǒng)中實現(xiàn)一致性是件有挑戰(zhàn)的事。經典的二階段提交、三階段提交都不能完美的解決這一問題,有關傳統(tǒng)的的分布式系統(tǒng)一致性問題可以看這里。Paxos 算法能完美地達到分布式系統(tǒng)的一致性,但由于較為復雜,在實際工程上不是很合適,Zab 協(xié)議借鑒了 Paxos 的思想,并進行了改進,以滿足工程上的實際需求。

設計目標

  • 一致性
  • 有序性:有序性是 Zab 協(xié)議與 Paxos 協(xié)議的一個核心區(qū)別。Zab 的有序性主要表現(xiàn)在兩個方面:
    1. 全局有序:如果消息 a 在消息 b 之前被投遞,那么在任何一臺服務器,消息 a都會在消息 b 之前被投遞。
    2. 因果有序:如果消息 a 在消息 b 之前發(fā)生(a 導致了 b),并被一起發(fā)送,則 a 始終在 b 之前被執(zhí)行。
  • 容錯性:有 2f+1 臺服務器,只要有大于等于 f+1 臺的服務器正常工作,就能完全正常工作。

協(xié)議內容

Zab 協(xié)議分為兩大塊:

  • 廣播(boardcast):Zab 協(xié)議中,所有的寫請求都由 leader 來處理。正常工作狀態(tài)下,leader 接收請求并通過廣播協(xié)議來處理。

  • 恢復(recovery):當服務初次啟動,或者 leader 節(jié)點掛了,系統(tǒng)就會進入恢復模式,直到選出了有合法數(shù)量 follower 的新 leader,然后新 leader 負責將整個系統(tǒng)同步到最新狀態(tài)。

    廣播(boardcast)

    廣播的過程實際上是一個簡化的二階段提交過程:

    1. Leader 接收到消息請求后,將消息賦予一個全局唯一的 64 位自增 id,叫做:zxid,通過 zxid 的大小比較即可實現(xiàn)因果有序這一特性。
    2. Leader 通過先進先出隊列(通過 TCP 協(xié)議來實現(xiàn),以此實現(xiàn)了全局有序這一特性)將帶有 zxid 的消息作為一個提案(proposal)分發(fā)給所有 follower。
    3. 當 follower 接收到 proposal,先將 proposal 寫到硬盤,寫硬盤成功后再向 leader 回一個 ACK。
    4. 當 leader 接收到合法數(shù)量的 ACKs 后,leader 就向所有 follower 發(fā)送 COMMIT 命令,同事會在本地執(zhí)行該消息。
    5. 當 follower 收到消息的 COMMIT 命令時,就會執(zhí)行該消息


      廣播過程

      相比于完整的二階段提交,Zab 協(xié)議最大的區(qū)別就是不能終止事務,follower 要么回 ACK 給 leader,要么拋棄 leader,在某一時刻,leader 的狀態(tài)與 follower 的狀態(tài)很可能不一致,因此它不能處理 leader 掛掉的情況,所以 Zab 協(xié)議引入了恢復模式來處理這一問題。從另一角度看,正因為 Zab 的廣播過程不需要終止事務,也就是說不需要所有 follower 都返回 ACK 才能進行 COMMIT,而是只需要合法數(shù)量(2f+1 臺服務器中的 f+1 臺) 的follower,也提升了整體的性能。

    恢復(recovery)

由于之前講的 Zab 協(xié)議的廣播部分不能處理 leader 掛掉的情況,Zab 協(xié)議引入了恢復模式來處理這一問題。為了使 leader 掛了后系統(tǒng)能正常工作,需要解決以下兩個問題:

  • 已經被處理的消息不能丟
  • 被丟棄的消息不能再次出現(xiàn)

已經被處理的消息不能丟

這一情況會出現(xiàn)在以下場景:當 leader 收到合法數(shù)量 follower 的 ACKs 后,就向各個 follower 廣播 COMMIT 命令,同時也會在本地執(zhí)行 COMMIT 并向連接的客戶端返回「成功」。但是如果在各個 follower 在收到 COMMIT 命令前 leader 就掛了,導致剩下的服務器并沒有執(zhí)行都這條消息。

如圖 1-1,消息 1 的 COMMIT 命令 Server1(leader)和 Server2(follower) 上執(zhí)行了,但是 Server3 還沒有收到消息 1 的 COMMIT 命令,此時 leader Server1 已經掛了,客戶端很可能已經收到消息 1 已經成功執(zhí)行的回復,經過恢復模式后需要保證所有機器都執(zhí)行了消息 1。


圖 1-1

為了實現(xiàn)已經被處理的消息不能丟這個目的,Zab 的恢復模式使用了以下的策略:

  1. 選舉擁有 proposal 最大值(即 zxid 最大) 的節(jié)點作為新的 leader:由于所有提案被 COMMIT 之前必須有合法數(shù)量的 follower ACK,即必須有合法數(shù)量的服務器的事務日志上有該提案的 proposal,因此,只要有合法數(shù)量的節(jié)點正常工作,就必然有一個節(jié)點保存了所有被 COMMIT 消息的 proposal 狀態(tài)。
  2. 新的 leader 將自己事務日志中 proposal 但未 COMMIT 的消息處理。
  3. 新的 leader 與 follower 建立先進先出的隊列, 先將自身有而 follower 沒有的 proposal 發(fā)送給 follower,再將這些 proposal 的 COMMIT 命令發(fā)送給 follower,以保證所有的 follower 都保存了所有的 proposal、所有的 follower 都處理了所有的消息。
    通過以上策略,能保證已經被處理的消息不會丟

被丟棄的消息不能再次出現(xiàn)

這一情況會出現(xiàn)在以下場景:當 leader 接收到消息請求生成 proposal 后就掛了,其他 follower 并沒有收到此 proposal,因此經過恢復模式重新選了 leader 后,這條消息是被跳過的。 此時,之前掛了的 leader 重新啟動并注冊成了 follower,他保留了被跳過消息的 proposal 狀態(tài),與整個系統(tǒng)的狀態(tài)是不一致的,需要將其刪除。

如圖 1-2 ,在 Server1 掛了后系統(tǒng)進入新的正常工作狀態(tài)后,消息 3被跳過,此時 Server1 中的 P3 需要被清除。


圖 1-2

Zab 通過巧妙的設計 zxid 來實現(xiàn)這一目的。一個 zxid 是64位,高 32 是紀元(epoch)編號,每經過一次 leader 選舉產生一個新的 leader,新 leader 會將 epoch 號 +1。低 32 位是消息計數(shù)器,每接收到一條消息這個值 +1,新 leader 選舉后這個值重置為 0。這樣設計的好處是舊的 leader 掛了后重啟,它不會被選舉為 leader,因為此時它的 zxid 肯定小于當前的新 leader。當舊的 leader 作為 follower 接入新的 leader 后,新的 leader 會讓它將所有的擁有舊的 epoch 號的未被 COMMIT 的 proposal 清除。

總結

個人認為 Zab 協(xié)議設計的優(yōu)秀之處有兩點,一是簡化二階段提交,提升了在正常工作情況下的性能;二是巧妙地利用率自增序列,簡化了異?;謴偷倪壿?,也很好地保證了順序處理這一特性。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容