當前位置:
首頁 > 最新 > ZooKeeper 設計原理

ZooKeeper 設計原理

大數據技術體系


編程語言:Java/Python/Scala

HDFS原理、MapReduce原理及編程、YARN原理、Hadoop集群搭建

Hive原理、HQL、自定義函數、數據倉庫設計

Spark原理、SparkStreaming編程、SparkSQL

Kafka原理、配置搭建、JavaAPI

Flume原理、搭建

Zookeeper原理、搭建


介紹完大數據的一些利好後,我們來看我們邁出的第一步:zookeeper。


什麼是zookeeper?

a service for coordinating(協調) processes of distributed applications,是一個重要的基礎服務,目標是從更底層提供一個簡單、高性能的服務,用來按需構建同步服務

zookeeper(動物管理員),為什麼叫這個名字?

zookeeper是Hadoop和Hbase的重要組件,hadoop裡面各種組件都是以動物命名的,而zookeeper相當於這動物園的管理員了

zookeeper特點是什麼?

提供了一組通用(generic)的,無等待的(wait-free)api,同時提供了兩個重要的特性:

保證每個客戶端請求FIFO

每個客戶端請求FIFO,所有事務請求線性有序

事務請求指能改變狀態的寫請求


我們先來回答為什麼需要 zookeeper?

在傳統的應用程序中,線程、進程的同步,都可以通過操作系統提供的機制來完成。但是在分散式系統中,多個進程之間的同步,操作系統層面就無能為力了。這時候就需要像ZooKeeper這樣的分散式的協調(Coordination)服務來協助完成同步。

分散式系統中對於 Coordination 提出了各種各樣的需求:

Con?guration:包括靜態的操作參數和動態的配置參數

Group membership:維護組中存活server的信息

leader election:維護每個server都負責什麼

解決上述coordination需求的一種方案是:為每種coordination需求都開發專門的服務。但是我們要知道一個道理:更powerful primitives的實現可以用於less powerful primitives,所以基於這個假設我們在設計coordination的服務上:我們不在實現具體的primitives,而是提供通用(generic)的API來實現滿足個性化的primitives,一旦作出這種決策,帶來的好處有兩點:

coordination kernel幫助我們在不改變服務核心的情況下實現新的primitives

根據應用需求提供更多樣化的primitives

在設計ZooKeeper的API的時候,我們移除了阻塞primitives,例如鎖,基於的考慮有如下兩點:

阻塞primitives會導致處理慢的客戶端影響相對較快的客戶端

由於請求在處理上依賴於其他客戶端的響應和失敗的檢查,那ZooKeeper本身實現上也會更複雜【一個客戶端鎖了,必須等待他釋放鎖。或者由於掉線強制釋放鎖】

ZooKeeper由於實現了wait-free的數據對象,從而和其他基於阻塞語義(blocking primitives)有了顯著的區別,ZooKeeper在組織wait-free的數據對象借鑒了文件系統的思路,將wait-free的數據對象按層級組織起來,不同只是移除了open和close這種阻塞方法。

ZooKeeper實現了pipelined architecture,提高了系統的吞吐。客戶端可以同時發出多個請求,非同步執行,同時保證請求的FIFO。

為了實現寫請求linearizable,實現了Zab協議,一個leader-based atomic broadcast protocol,但是對於讀請求,我們不適用Zab,只是本地讀,這樣能很方便的擴展系統。

在客戶端緩存數據可以有效的提高系統性能,但是緩存的數據怎麼更新呢?ZooKeeper使用watch機制,不直接操作客戶端緩存,這是因為:由於Chubby直接管理客戶端緩存,一旦某個客戶端處理慢了(可能是掛了),會導致阻塞數據更新。針對這個問題,Chubby 使用租期來解決,一旦某個客戶端有錯誤,不會影響更新操作太長時間,但這也只是確定了影響的上限,無法避免,而ZooKeeper的watches可以徹底解決改問題。

註:Chubby 是什麼?

Google的三篇論文影響了很多很多人,也影響了很多很多系統。這三篇論文一直是分散式領域傳閱的經典。根據MapReduce,於是我們有了Hadoop;根據GFS,於是我們有了HDFS;根據BigTable,於是我們有了HBase。而在這三篇論文里都提及Google的一個lock service—Chubby,於是我們有了Zookeeper。

總結起來,本文的主要內容是:

Coordination kernel:提出了wait-free的 Coordination service,能夠保證 relaxed consistency,為其他同步技術提供基本原語。

Coordination recipes:通過 ZooKeeper 可以實現high level的同步原語,包括了強同步和強一致的同步(zookeeper本身提供的是wait-free的同步原語)

Experience with Coordination:心得,具體案例和評測


ZooKeeper提供了client library來訪問服務,client library主要做兩件事:

管理client和ZooKeeper之間的網路連接

提供ZooKeeper的api

術語:

client:a user of the ZooKeeper service

server:a process providing the ZooKeeper service

znode:an in-memory data node in the ZooKeeper data

data tree:像文件系統一樣按層級組織的命名空間

update,write:改變data tree狀態的操作

session:client和ZooKeeper之間的網路連接


ZooKeeper給客戶端提供了znode的抽象,客戶端通過api來操作znode中存儲的數據,znode的地址類似文件系統中的path,像上圖中節點p_1就通過路徑/app1/p_1來訪問,客戶端可以創建兩種znode:

Regular: 需要客戶端顯式的創建和刪除

ephemeral: 客戶端創建,也可以刪除,也可以當會話終止時候讓系統自動刪除

watches怎麼創建?

讀請求上設置watch參數

watches作用?

客戶端不必輪詢伺服器獲取數據,當數據發生改變的時候,通知客戶端

watches什麼時候失效?

當數據發生改變通知客戶端後

session關閉

watches通知了什麼?

watches通知只是告知狀態改變了,但是不提供改變的數據

數據模型

如圖一所示:類似於文件系統,但是znodes不是用來做數據存儲的,而是用來跟實際的應用映射的,像圖1中,有兩個應用app1,app2,app1下面實現了個簡單的group membership protocol。

雖然znode設計之初不是為了存儲數據,但是也可以存儲一些meta-data或者con?guration信息,同時znode本身會存儲time stamps 和 version counters等元信息

會話(sessions)

代表client和ZooKeeper之間的網路連接,作用有:

server端可以通過sessions超時來判斷客戶端是否健在

客戶端可以通過sessions觀察其操作的一連串變化

sessions使得client的連接可以從一個server透明的轉移到另一個server,因此可以持續的提供client服務


以上所有操作有syn和asyn兩個版本。ZooKeeper的客戶端保證所有寫操作是完全有序的,寫操作後其他client的寫能看到。

在訪問的znode的時候都是通過完整的path來訪問的,而不是像文件系統那樣通過open,close來操作文件句柄,大大簡化了servers端的複雜度,不需要保存額外的信息了。


Linearizable writes:所有寫請求有序

FIFO client order:每個客戶端請求FIFO

考慮場景:leader election

當新的leader產生的時候,需要改變大量的配置後,通知其他processes,需要滿足兩個要求:

新leader改變配置的時候,其他processes不能讀取不完整的配置

新leader在改變配置過程中掛了,其他processes不能使用這個不完整的配置

通過鎖能滿足第一個需求,zookeeper的實現:

新leader改變前刪除 ready znode

改變配置(通過pipeline加速)

新建 ready znode

因為寫順序的保證,其他客戶端能看到ready的時候,肯定新配置也生效了,如果在更改配置中leader掛了,就不會有ready。

上面仍然有一個問題:如果process先是看到了ready,此時在讀取之前,leader刪除了ready,開始更改配置,那process會讀取到不完整的配置了,怎麼解決呢?

這是通過對通知的順序性保證解決的,具體來說就是:如果客戶端在watch一個Ready改變事件,那只有當配置改變後,才會通知client Ready有變化的事件(不是Ready刪除就通知事件),這就保證了客戶端收到通知,肯定是配置變化了。

另一個可能的問題是:客戶端之間除了ZooKeeper之外,還有別的通信通道,場景是:

A和B在ZooKeeper上有共享數據,A改變數據後,通過其他通信手段告訴B數據改變了,此時B去讀取數據,可能會讀取不到改變的數據,因為ZooKeeper集群可能存在的主從延遲,解決方案是:B讀之前先發個sync請求,類似於文件系統中的flush操作,讓pending的寫請求真正執行。

除此之外,ZooKeeper還有兩個保證:

高可用,只要大多數機器還存活,就能提供服務

數據可靠:只要ZooKeeper回複寫成功,則數據最終一定會存在在伺服器上


作為一個 coordination,非常重要的就是高可用和數據可靠性,我們來看下如何實現的。

先來看數據的寫入過程:

客戶端提交寫請求

follower寫請求交給leader,由leader作為整個事務的協調者,負責整個寫入過程

leader在整個事務中是通過ZAB演算法保證了數據的最終一致,由leader發起事務提議(重點是一個zxid,全局遞增的一個id生成器,通過zxid來達到全局時鐘的效果)

follower接收到leader發起的事務提議,返回收到(所有請求都是在一個FIFO的隊列中)

leader在收到follower的回復後,提交本次事務

客戶端收到回復

此處ZAB演算法是保證數據一致性的關鍵,我們在raft那再講。


本文主要是介紹了zookeeper是什麼:一個開源的針對大型分散式系統的可靠協調系統;設計目標是:將複雜且容易出錯的分散式式一致性服務封裝起來,構成一個高效可靠的原語集,並以簡單易用的介面提供給用戶使用,其特性有:

最終一致性

順序性:從同一個客戶端發起的事務請求,最終會嚴格地按照其發送順序被應用到Zookeeper中。

可靠性:一旦伺服器成功的應用一個事務,並完成了客戶端的響應,那麼該事務所引起的服務端狀態變更將會被一直保留下去。

實時性:Zookeeper不能保證兩個客戶端能同時得到剛更新的數據,如果需要最新數據,應該在讀數據之前調用sync()介面。

原子性:一次數據更新要麼成功,要麼失敗。

單一視圖:無論客戶端連接到哪個伺服器,看到的數據模型都是一致的。

其中一致性演算法將會在raft中講解。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 互聯網後端架構 的精彩文章:

TAG:互聯網後端架構 |