ZooKeeper Internals -- ZooKeeper內部工做方式

ZooKeeper Internals
介紹
原子廣播
保證,屬性和定義
領導者激活
活動消息
摘要
比較
法定人數
記錄
開發者指南
記錄在正確的級別
使用標準的slf4j成語git

介紹
本文檔包含有關ZooKeeper內部工做方式的信息。到目前爲止,它討論瞭如下主題:github

原子廣播
記錄算法

原子廣播
ZooKeeper的核心是一個原子消息系統,可使全部服務器保持同步。apache

保證,屬性和定義
ZooKeeper使用的消息傳遞系統提供的特定保證以下:api

可靠的交付:若是消息m由一臺服務器提供,它將最終由全部服務器提供。數組

總訂單:若是一條服務器在消息b以前傳遞消息,則全部服務器將在b以前傳送消息。若是a和b是傳遞的消息,則a將在b以前傳遞或b將在傳遞以前傳遞。服務器

因果順序:若是在b的發送者發送消息a以後發送消息b,則必須在b以前訂購。若是發送方在發送b後發送c,則必須在b以後訂購c。markdown

ZooKeeper消息傳遞系統還須要高效,可靠,易於實施和維護。咱們大量使用消息傳遞,所以咱們須要系統可以每秒處理數千個請求。雖然咱們能夠要求至少k + 1個正確的服務器來發送新消息,但咱們必須可以從相關故障中恢復,例如斷電。當咱們實施該系統時,咱們幾乎沒有時間和不多的工程資源,所以咱們須要一個工程師能夠訪問的協議,而且易於實現。咱們發現咱們的協議知足了全部這些目標。app

咱們的協議假設咱們能夠在服務器之間構建點對點FIFO通道。雖然相似的服務一般假設消息傳遞可能丟失或從新排序消息,但咱們假設FIFO通道很是實用,由於咱們使用TCP進行通訊。具體來講,咱們依賴TCP的如下屬性:異步

有序傳遞:數據按照發送的順序傳送,只有在傳送完m以前發送的全部郵件以後才傳送消息m。(這樣作的必然結果是,若是消息m丟失,m以後的全部消息都將丟失。)

關閉後沒有消息:一旦FIFO通道關閉,將不會收到任何消息。

FLP證實,若是可能出現故障,則沒法在異步分佈式系統中實現共識。爲確保咱們在出現故障時達成共識,咱們會使用超時。可是,咱們依靠活力時間而不是正確性。所以,若是超時中止工做(例如時鐘故障),則消息傳遞系統可能會掛起,但不會違反其保證。

在描述ZooKeeper消息傳遞協議時,咱們將討論數據包,提議和消息:

數據包:經過FIFO通道發送的字節序列

提案:協議單位。經過與法定數量的ZooKeeper服務器交換數據包來商定提案。大多數提案都包含消息,但NEW_LEADER提案是與消息不對應的提案示例。

消息:要以原子方式廣播到全部ZooKeeper服務器的字節序列。在提交以前提交併贊成的消息。

如上所述,ZooKeeper保證了消息的總順序,而且它還保證了提議的總順序。ZooKeeper使用ZooKeeper事務id(zxid)公開總排序。全部提案在提議時都會加蓋zxid,並準確反映總排序。提案將發送到全部ZooKeeper服務器,並在法定人數確認提案時提交。若是提案包含消息,則在提交提案時將傳遞消息。確認意味着服務器已將提議記錄到持久存儲。咱們的法定人數要求任何一對仲裁必須至少有一個共同的服務器。咱們經過要求全部法定人數的大小(n / 2 + 1)來確保這一點)其中n是組成ZooKeeper服務的服務器數量。

zxid有兩個部分:紀元和計數器。在咱們的實現中,zxid是一個64位數字。咱們使用高階32位用於紀元,低階32位用於計數器。由於它有兩個部分表明zxid既是數字又是一對整數,(epoch,count)。時代數字表明瞭領導層的變化。每當新領導人上臺時,它將擁有本身的紀元號碼。咱們有一個簡單的算法來爲一個提議分配一個惟一的zxid:領導者只需遞增zxid以得到每一個提案的惟一zxid。領導激活將確保只有一個領導者使用給定的紀元,所以咱們的簡單算法保證每一個提案都具備惟一的ID。

ZooKeeper消息傳遞包含兩個階段:

領導者激活:在此階段,領導者創建正確的系統狀態,並準備開始提出建議。

主動消息傳遞:在此階段,領導者接受建議和協調消息傳遞的消息。

ZooKeeper是一個總體協議。咱們不關注個別提案,而是關注整個提案流。咱們嚴格的訂購使咱們可以有效地完成這項工做並大大簡化咱們的協議。領導激活體現了這種總體概念。領導者只有在達到法定數量的粉絲時纔會變得活躍(領導者也算做跟隨者。你老是能夠爲本身投票)與領導者同步,他們擁有相同的狀態。該州包括領導者認爲已經提交的全部提案以及跟隨領導者的提議,NEW_LEADER提案。(但願你是在想本身,領導者認爲已提交的提案包括全部真正提交的提案嗎?答案是確定的。下面,咱們說明緣由。)

領導者激活
領導者激活包括領導者選舉。咱們目前在ZooKeeper中有兩個領導者選舉算法:LeaderElection和FastLeaderElection(AuthFastLeaderElection是FastLeaderElection的變體,它使用UDP並容許服務器執行簡單形式的身份驗證以免IP欺騙)。只要符合如下條件,ZooKeeper消息傳遞並不關心選擇領導者的確切方法:

領導者已經看到了全部粉絲中最高的zxid。
法定數量的服務器已承諾跟隨領導者。
在這兩個要求中,只有第一個,跟隨者須要保持正確操做的最高zxid。第二個要求,即法定數量的追隨者,只須要很高的機率。咱們將從新檢查第二個要求,所以若是在領導者選舉期間或以後發生失敗而且法定人數丟失,咱們將經過放棄領導者激活和進行另外一次選舉來恢復。

領導者選舉後,單個服務器將被指定爲領導者,並開始等待粉絲鏈接。其他服務器將嘗試鏈接到領導者。領導者將經過發送他們遺失的任何提案與追隨者同步,或者若是追隨者缺乏太多提案,它將向關注者發送狀態的完整快照。

有一個角落案例,其中有一個跟隨者有提議,U,領導人看不到。提案按順序排列,所以U的提案的zxids高於領導者看到的zxids。追隨者必須在領導人選舉後到達,不然追隨者將被選爲領導者,由於它已經看到更高的zxid。因爲提交的提案必須由法定數量的服務器看到,而且選出領導者的法定數量的服務器沒有看到U,所以您的提議還沒有提交,所以能夠將其丟棄。當追隨者鏈接到領導者時,領導者將告訴追隨者丟棄U.

一個新的領導者創建一個zxid​​來開始使用新的提議,經過得到它所見過的最高zxid的時代e,並設置下一個zxid​​用於(e + 1,0),領導者與跟隨者同步後,它將提出一個NEW_LEADER提案。提交NEW_LEADER提案後,領導者將激活並開始接收和發佈提案。

這聽起來很複雜,但這裏是領導者激活過程當中的基本操做規則:

跟隨者將在與領導者同步後確認NEW_LEADER提案。
跟隨者只會使用來自單個服務器的給定zxid確認NEW_LEADER提議。
當法定​​數量的粉絲確認後,新的領導者將提交NEW_LEADER提案。
當NEW_LEADER提議爲COMMIT時,關注者將提交從領導者收到的任何州。
在NEW_LEADER提案得到COMMITED以前,新領導者不會接受新提案。
若是領導者選舉錯誤地終止,咱們就沒有問題,由於因爲領導者沒有法定人數,所以不會提交NEW_LEADER提案。當發生這種狀況時,領導者和任何剩下的追隨者將超時並返回領導者選舉。

活動消息
領導者激活完成全部繁重的工做。一旦領導者加冕,他就能夠開始提出提案。只要他仍然是領導者,就不可能出現其餘領導者,由於沒有其餘領導者可以得到法定數量的粉絲。若是新的領導者確實出現,那就意味着領導者失去了法定人數,新的領導者將清理領導激活期間留下的任何混亂。

ZooKeeper消息傳遞的操做相似於傳統的兩階段提交。

全部通訊通道都是FIFO,因此一切都按順序完成。具體而言,遵照如下操做約束:

領導者使用相同的訂單向全部粉絲髮送提案。此外,此順序遵循收到請求的順序。由於咱們使用FIFO通道,這意味着關注者也會按順序接收提案。
關注者按照收到的順序處理消息。這意味着將按順序確認消息,而且因爲FIFO通道,領導者將按順序接收來自關注者的ACK。這也意味着若是消息$ m $已寫入非易失性存儲器,則在$ m $以前建議的全部消息都已寫入非易失性存儲器。
一旦有法定數量的粉絲確認消息,領導者將向全部粉絲髮出COMMIT。因爲消息按順序被確認,所以將由跟隨者按順序接收的領導者發送COMMIT。
COMMIT按順序處理。在提交提案時,關注者會發送提案消息。

摘要
你去吧 它爲何有效?具體而言,爲何新領導人認爲的一系列提案老是包含任何實際提交的提案?首先,全部提議都有一個惟一的zxid,所以與其餘協議不一樣,咱們永遠沒必要擔憂爲同一個zxid​​提出兩個不一樣的值; 粉絲(領導者也是粉絲)按順序查看和記錄提案; 提案按順序提交; 因爲粉絲一次只跟隨一位領導者,因此每次只有一位活躍的領導者; 一位新領導人已經看到了上一個時代的全部承諾提案,由於它已經從法定數量的服務器中看到了最高的zxid; 新領導人看到的上一個時代的任何未提出的提議都將由該領導者在活躍以前承諾。

比較
這不僅是Multi-Paxos嗎?不,Multi-Paxos須要一些方法來確保只有一個協調員。咱們不期望這種保證。相反,咱們使用領導者激活來恢復領導層變革,或者讓老領導人相信他們仍然活躍。

這不只僅是Paxos嗎?您的活動消息傳遞階段看起來就像Paxos的第2階段?實際上,對咱們來講,主動消息傳遞看起來就像是2階段提交,而不須要處理停止。主動消息傳遞與它們具備交叉提議排序要求的意義不一樣。若是咱們不對全部數據包保持嚴格的FIFO排序,它就會崩潰。此外,咱們的領導者激活階段與他們兩個都不一樣。特別是,咱們對epochs的使用容許咱們跳過未提交的提議塊,而不用擔憂給定zxid的重複提議。

法定人數
原子廣播和領導者選舉使用法定人數的概念來保​​證系統的一致視圖。默認狀況下,ZooKeeper使用多數仲裁,這意味着在其中一個協議中發生的每一個投票都須要多數投票。一個例子是認可領導者提案:領導者只有在收到法定數量的服務器的確認後才能提交。

若是咱們從使用多數性中提取咱們真正須要的屬性,咱們只須要保證用於經過投票驗證操做的進程組(例如,確認領導者提議)在至少一個服務器中成對交叉。使用多數人保證這樣的財產。可是,還有其餘方法能夠構建與多數羣體不一樣的法定人數。例如,咱們能夠爲服務器的投票分配權重,並說一些服務器的投票更重要。爲了得到法定人數,咱們獲得足夠的票數,以便全部投票的權重總和大於全部權重總和的一半。

使用權重而且在廣域部署(共址)中有用的不一樣結構是分層結構。經過這種構造,咱們將服務器分紅不相交的組併爲進程分配權重。爲了造成法定人數,咱們必須從大多數G組得到足夠的服務器,這樣對於G中的每一個組g,來自g的投票總和大於g中權重總和的一半。有趣的是,這種結構能夠實現更小的法定人數。例如,若是咱們有9個服務器,咱們將它們分紅3組,併爲每一個服務器分配1的權重,而後咱們就能夠造成大小爲4的仲裁。注意,兩個進程的子集各佔大多數來自大多數組中的每一個組的服務器必然具備非空交叉點。

使用ZooKeeper,咱們爲用戶提供配置服務器以使用多數仲裁,權重或組層次結構的功能。

記錄
Zookeeper使用slf4j做爲日誌記錄的抽象層。如今選擇版本1.2中的log4j做爲最終的日誌記錄實現。爲了更好地嵌入支持,計劃未來決定爲最終用戶選擇最終的日誌記錄實現。所以,始終使用slf4j api在代碼中編寫日誌語句,但配置log4j以瞭解如何在運行時進行日誌記錄。請注意,slf4j沒有FATAL級別,FATAL級別的舊消息已移至ERROR級別。有關爲ZooKeeper配置log4j的信息,請參閱ZooKeeper管理員指南的「 日誌記錄」部分。

開發者指南
在代碼中建立日誌語句時,請遵循 slf4j手冊。 在建立日誌語句時,請閱讀有關性能的常見問題解答。補丁審閱者將查找如下內容:

記錄在正確的級別

slf4j中有多個級別的日誌記錄。

選擇正確的一個很重要。按從高到低的順序:

ERROR級別指定可能仍容許應用程序繼續運行的錯誤事件。
WARN級別表示潛在的有害狀況。
INFO級別指定信息性消息,以粗粒度級別突出顯示應用程序的進度。
DEBUG Level指定對調試應用程序最有用的細粒度信息事件。
TRACE Level指定比DEBUG更細粒度的信息事件。
ZooKeeper一般在生產中運行,以便將INFO級別嚴重性和更高(更嚴重)的日誌消息輸出到日誌。

使用標準的slf4j成語

靜態消息記錄

LOG.debug("process completed successfully!");
可是,當須要建立參數化消息時,請使用格式化錨點。

LOG.debug("got {} messages in {} minutes",new Object[]{count,time});
命名

記錄器應以其使用的類命名。

public class Foo {

private static final Logger LOG = LoggerFactory.getLogger(Foo.class);
....
public Foo() {
    LOG.info("constructing Foo");

異常處理

try {

// code

} catch (XYZException e) {

// do this
LOG.error("Something bad happened", e);
// don't do this (generally)
// LOG.error(e);
// why? because "don't do" case hides the stack trace

// continue process here as you need... recover or (re)throw

}

轉載來源:https://github.com/apache/zoo...

相關文章
相關標籤/搜索