Zookeeper和etcd使用場景

1、Zookeeper和etcd共同點

Zookeeper和etcd的功能和使用場景都很類似。

2、Zookeeper選主方法

Paxos & fast paxos

參考 Paxos算法與Zookeeper分析

3、Zookeeper復制數據方法

Zab

Zab協議有兩種模式,分別是恢復模式(選主)和廣播模式(同步)。當服務啟動或者在leader崩潰后,Zab就進入了恢復模式,當leader被選舉出來,且大多數Server完成了和leader的狀態同步以后,恢復模式就結束了。狀態同步保證了leader和follower具有相同的系統狀態。

廣播模式

Leader向follower發送請求:

  1. Leader向follower依次發送prepare請求,并等待回應,半數以上回復即prepare成功。
  2. Leader按相同順序依次發送commit請求,并等待回應,半數以上回復即commit成功。

如果client連上的是follower,寫請求會轉發給leader處理,讀請求如果要讀到最新數據也需要轉發給leader,如果不需要可以直接在follower中讀取。

恢復模式

這種情況主要解決的是新老交互的問題,即新leader是否需要繼續老leader未完成的狀態。

這里要看老leader掛掉時的情況:

  1. 多數follower還沒有收到老leader的commit。
  2. 多數follower已經收到老leader的commit,并且操作完成。

第一種情況,因為多數follower還沒有commit,該commit失敗。完成commit的server需要在新leader選出后將該commit回滾。

第二種情況,新leader通過一個多數派獲得老leader提交的最新數據,老leader重啟后,可能還會認為自己是leader,可能會繼續發送未完成的請求,從而因為兩個leader同時存在導致算法過程失敗,解決辦法是把leader信息加入每條消息的id中,Zookeeper中稱為zxid,zxid為一64位數字,高32位為leader信息又稱為epoch,每次leader轉換時遞增;低32位為消息編號,leader轉換時應該從0重新開始編號。通過zxid,follower能很容易發現請求是否來自老leader,從而拒絕老leader的請求。新leader首先要獲得大多數節點的支持,然后從狀態最新的節點同步事務(如何同步見下文),完成后才可正式成為leader發起事務。

選出新leader以后,zookeeper就進入狀態同步過程:

  1. Leader等待server連接;
  2. Follower連接leader,將最大的zxid發送給leader;
  3. Leader根據follower的zxid確定同步點(需要多數派都在這種狀態或更新狀態)
  4. 完成同步后通知follower已經成為uptodate狀態;
  5. Follower收到uptodate消息后,又可以重新接受client的請求進行服務了。

4、Etcd選主方法

Raftppt-understandable-cons-protocol.pdf

5、Etcd復制數據方法

Raftppt-understandable-cons-protocol.pdf

6、Zookeeper常見使用場景

配置管理、數據分發與訂閱

管理配置是zookeeper最常見的應用場景。比如一個服務的多個實例部署在不同機器上,如果使用本地配置文件,修改、實例遷移等操作都比較麻煩,可以把配置存放到zookeeper的某個路徑下。每個實例啟動后都去zookeeper讀取配置,如果需要追蹤配置的更新,可以監視節點的變更。

這個配置不局限為服務運行所需要的配置信息,符合分發、訂閱方法使用的數據都可,也可以指不同服務實例運行時的中間狀態(對應單機進程使用的內存中狀態數據或者狀態文件),這樣各個實例可以實現為無狀態的,一旦實例掛掉不會丟失數據,方便服務遷移(具體遷移方式參考集群管理部分)。

集群管理與Master選舉

集群機器監控

這通常用于那種對集群中機器狀態,機器在線率有較高要求的場景,能夠快速對集群中機器變化作出響應。這樣的場景中,往往有一個監控系統,實時檢測集群機器是否存活。

利用ZooKeeper可以實現一種集群機器存活性監控系統:

  1. 每個機器在/Machines/下創建一個臨時節點(如/Machines/${hostname},可以把具體的狀態作為節點內容)。
  2. 服務端監視/Machines/下節點的變動,來判斷機器是否存活以及具體狀態。
Master選舉

在分布式環境中,有些業務邏輯只需要集群中的某一臺機器進行執行,其他的機器可以共享這個結果,這樣可以大大減少重復計算,提高性能,于是就需要進行master選舉。

利用ZooKeeper的強一致性,能夠保證在分布式高并發情況下節點創建的全局唯一性,即:同時有多個客戶端請求創建 /currentMaster 節點,最終一定只有一個客戶端請求能夠創建成功。利用這個特性,就能很輕易的在分布式環境中進行集群選舉了。

此外,也可以利用Zookeeper的EPHEMERAL_SEQUENTIAL節點,實現動態選舉:每個客戶端都在/Master/下創建一個EPHEMERAL_SEQUENTIAL節點,由于ZooKeeper保證SEQUENTIAL的有序性,因此我們可以簡單的把節點號最小的作為Master,就完成了選主。

分布式鎖

屏障

似乎不是很常用,參考官方文檔(http://zookeeper.apache.org/doc/r3.4.6/recipes.html#sc_recipes_eventHandles

獨占鎖

獲取鎖時如果不需要等待(只使用try_lock()、unlock(),不使用lock()),可以直接以一個節點(如/Lock,事先不存在)作為鎖,試圖獲取這個鎖的客戶端直接創建這個節點,只有一個可以成功,使用完了則刪除這個節點(或者使用臨時節點,關閉會話)。如果需要等待,參考控制時序的鎖。

控制時序的鎖

就是所有試圖來獲取這個鎖的客戶端,最終都會獲得(除非有人一直不釋放),只是有個全局時序。

預先創建/Lock/,客戶端在它下面創建臨時有序節點lock-(Zk的父節點維持一份子節點創建的時序),然后獲取/Lock/下的子節點(設置監視標志),直到自己擁有的是最小的序號則獲得鎖,釋放時只需要刪除該節點。

如果每個客戶端都不可撤銷鎖(包括設置獲取鎖的超時時間),也可以改成這樣。預先創建/Lock/,客戶端在它下面創建有序節點lock-(非臨時,否則某還沒得到鎖的客戶端會話意外關閉后,會使得下一個客戶端獲得鎖,導致混亂),然后獲取/Lock/下的子節點(不設置監視標志),如果自己擁有的是最小的序號則獲得鎖,否則只監視比自己小的最大子節點,直到該節點被刪除后獲得鎖,釋放時刪除自己的節點。和前一種方法相比,每次鎖被釋放時只有下一個獲取該鎖的客戶端被喚醒。

共享鎖(讀寫鎖)

獲取讀鎖過程:

  1. 在/Lock/(/Lock/是預先創建的)下創建有序節點read-。
  2. 獲得/Lock/下的子節點(不設置監視標志)。
  3. 如果沒有以write-開頭并且比自己小(一個目錄里的序號是統一的,不區分節點名)的節點,則獲得了讀鎖。
  4. 否則監視這個以write-開頭并且比自己小的節點,直到這個節點被刪除了,返回2

獲取寫鎖的過程:

  1. 在/Lock/下創建有序節點write-。
  2. 獲得/Lock/下的子節點(不設置監視標志)。
  3. 如果沒有比自己小的節點,則獲得了寫鎖。
  4. 否則監視這個以比自己小的節點,直到這個節點被刪除了,返回2

但前提是每個客戶端都不可撤銷鎖(包括設置獲取鎖的超時時間),否則請參考Shared+Locks

名字服務

這個主要是作為分布式命名服務,通過調用zk的create node api,能夠很容易創建一個全局唯一的path,這個path就可以作為一個名稱。

命名服務是指通過指定的名字來獲取資源或者服務的地址,提供者的信息。利用Zookeeper很容易創建一個全局的路徑,而這個路徑就可以作為一個名字,它可以指向集群中的集群,提供的服務的地址,遠程對象等。簡單來說使用Zookeeper做命名服務就是用路徑作為名字,路徑上的數據就是其名字指向的實體。

服務提供者在啟動的時候,向ZK上的指定節點(如/${serviceName}/providers)目錄下寫入自己的URL地址,這個操作就完成了服務的發布。
服務消費者啟動的時候,訂閱/${serviceName}/providers/目錄下的提供者URL地址(如果一次性使用直接讀取即可),并向/${serviceName}/consumers/目錄下寫入自己的URL地址(如果服務提供者者需要通過ZK獲取服務消費者身份,可選)。
所有向ZK上注冊的地址都是臨時節點,這樣就能夠保證服務提供者和消費者能夠自動感應資源的變化。

典型例子(參考http://blog.csdn.net/qq910894904/article/details/40833859,省略其中的服務監控者):
nameservice:
-m 程序運行的方式,指定是服務提供者provider還是服務消費者consumer
-n 服務名稱
-s Zookeeper的服務地址IP:PORT

服務提供者:

nameservice -m provider -n ServiceDemo -s 172.17.0.36:2181

服務消費者:

nameservice -m consumer -n ServiceDemo -s 172.17.0.36:2181

第一條命令啟動服務提供者,它提供一個ServiceDemo的服務,首次啟動后會創建/NameService/、/NameService/ServiceDemo/、/NameService/ServiceDemo/provider/幾個路徑(永久節點)。然后在服務提供進程在/NameService/ServiceDemo/provider/下創建臨時序列節點。

第二條命令啟動一個服務消費進程,它在/NameService/ServiceDemo/consumer/下創建臨時序列節點,并watch /NameService/ServiceDemo/provider/下的子節點變化事件,及時更新provider列表。

分布式通知/協調

通知的典型實現方式是被通知方監聽一個端口,等待通知方往端口發送消息。雙方耦合比較嚴重,如果雙方需要互相通知,有多個通知方和被通知方自己交互則邏輯復雜、難于實現。

典型的通知就是被通知方監視一個節點,通知方修改或者刪除這個節點。

分布式隊列

隊列

預先創建/Queue/。

push:創建一個有序的/Queue/queue-節點。
pop:取出/Queue/下序列號最小的節點(先獲取到所有節點,然后刪除序號最小的節點表示取出,如果刪除之前節點已經被別人刪了也沒事,直接跳過。也可以獲取所有節點是設置監視標志,這樣可以及時處理新添加的節點)。

zookeeeper源碼的recipes/queue目錄有示例。

優先隊列

在上邊普通隊列的基礎上作2個小改動即可實現優先隊列。

  1. push時,創建以queue-x-的節點,x是代表優先級的數字。
  2. pop時,取出x最小的節點,而不是序列號最小的節點。

7、Etcd常見使用場景

Etcd和zookeeper的使用場景大致相同,主要不同在于接口和部分周邊功能上。

8、Zookeeper和etcd使用場景比較

Etcd支持更方便的HTTP API,多語言支持比zookeeper也要好一些。

Etcd沒有acl權限控制。

性能上,需要自己根據使用場景做測試。

9、參考

付費解決 Windows、Linux、Shell、C、C++、AHK、Python、JavaScript、Lua 等領域相關問題,靈活定價,歡迎咨詢,微信 ly50247。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容