1. 程式人生 > >同是ZooKeeper,你和架構師的理解差在哪裡?

同是ZooKeeper,你和架構師的理解差在哪裡?

前言

提到ZooKeeper,相信大家都不會陌生。DubboKafka,Hadoop等等專案裡都能看到它的影子。但是你真的瞭解 ZooKeeper 嗎?如果面試官讓你給他講講 ZooKeeper 是個什麼東西,你能回答到什麼地步呢?

而且,同樣是ZooKeeper,一線架構師和你的理解又有哪些不同呢?

  • 如何從一個問題及思考方式瞭解架構的本質?
  • 如何剖析ZooKeeper為什麼這麼設計?
  • ZooKeeper到底能幹嘛?

如果你已經工作了2-3年,有沒有思考過上述3個問題?

——如果有,你的答案會是?


——如果沒有,為什麼?

下面,我主要介紹一下ZooKeeper是什麼以及ZooKeeper的作用,前面兩個問題,我會在文末給出答案。

一、ZooKeeper是什麼

ZooKeeper 是一個分散式的,開放原始碼的分散式應用程式協調服務,是Google的Chubby一個開源的實現,是Hadoop和Hbase的重要元件。它是一個為分散式應用提供一致性服務的軟體,提供的功能包括:配置維護、域名服務、分散式同步、組服務等。

官網:http://zookeeper.apache.org/
原始碼:https://github.com/apache/zookeeper

ZooKeeper翻譯成中文是“動物園管理員

”,至於為什麼這麼翻譯,感興趣的話可以去看看《從Paxos到Zookeeper 》 ,本文中很多的名詞介紹也來自本書。

三、ZooKeeper的特性

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

  • 原子性,所有事務請求的處理結果在整個叢集中所有機器上的應用情況是一致的,即整個叢集要麼都成功應用了某個事務,要麼都沒有應用。

  • 單一檢視,無論客戶端連線的是哪個 Zookeeper 伺服器,其看到的服務端資料模型都是一致的。

  • 可靠性,一旦服務端成功地應用了一個事務,並完成對客戶端的響應,那麼該事務所引起的服務端狀態變更將會一直被保留,除非有另一個事務對其進行了變更。

  • 實時性,Zookeeper 保證在一定的時間段內,客戶端最終一定能夠從服務端上讀取到最新的資料狀態。

四、ZooKeeper的設計目標

  • 簡單的資料結構
    Zookeeper 使得分散式程式能夠通過一個共享的樹形結構的名字空間來進行相互協調,即Zookeeper 伺服器記憶體中的資料模型由一系列被稱為ZNode的資料節點組成,Zookeeper 將全量的資料儲存在記憶體中,以此來提高伺服器吞吐、減少延遲的目的。

  • 可以構建叢集
    Zookeeper 叢集通常由一組機器構成,組成 Zookeeper 叢集的而每臺機器都會在記憶體中維護當前伺服器狀態,並且每臺機器之間都相互通訊。

  • 順序訪問
    對於來自客戶端的每個更新請求,Zookeeper 都會分配一個全域性唯一的遞增編號,這個編號反映了所有事務操作的先後順序。

  • 高效能
    Zookeeper 和Redis一樣全量資料儲存在記憶體中,100%讀請求壓測QPS 12-13W。

五、關於 ZooKeeper 的一些重要概念

5.1 Zookeeper 叢集:

Zookeeper 是一個由多個 server 組成的叢集,一個 leader,多個 follower。(這個不同於我們常見的Master/Slave模式)leader 為客戶端伺服器提供讀寫服務,除了leader外其他的機器只能提供讀服務。
每個 server 儲存一份資料副本全資料一致,分散式讀 follower,寫由 leader 實施更新請求轉發,由 leader 實施更新請求順序進行,來自同一個 client 的更新請求按其傳送順序依次執行資料更新原子性,一次資料更新要麼成功,要麼失敗。全域性唯一資料檢視,client 無論連線到哪個 server,資料檢視都是一致的實時性,在一定事件範圍內,client 能讀到最新資料。

 

 

5.2 叢集角色

Leader:是整個 Zookeeper 叢集工作機制中的核心 。Leader 作為整個 ZooKeeper 叢集的主節點,負責響應所有對 ZooKeeper 狀態變更的請求。
主要工作:

  • 事務請求的唯一排程和處理,保障叢集處理事務的順序性。
  • 叢集內各伺服器的排程者。

Leader 選舉是 Zookeeper 最重要的技術之一,也是保障分散式資料一致性的關鍵所在。我們以三臺機器為例,在伺服器叢集初始化階段,當有一臺伺服器Server1啟動時候是無法完成選舉的,當第二臺機器 Server2 啟動後兩臺機器能互相通訊,每臺機器都試圖找到一個leader,於是便進入了 leader 選舉流程.

  1. 每個 server 發出一個投票
    投票的最基本元素是(SID-伺服器id,ZXID-事物id)
  2. 接受來自各個伺服器的投票
  3. 處理投票
    優先檢查 ZXID(資料越新ZXID越大),ZXID比較大的作為leader,ZXID一樣的情況下比較SID
  4. 統計投票
    這裡有個過半的概念,大於叢集機器數量的一半,即大於或等於(n/2+1),我們這裡的由三臺,大於等於2即為達到“過半”的要求。
    這裡也有引申到為什麼 Zookeeper 叢集推薦是單數。
叢集數量 至少正常執行數量 允許掛掉的數量
2 2的半數為1,半數以上最少為2 0
3 3的半數為1.5,半數以上最少為2 1
4 4的半數為2,半數以上最少為3 1
5 5的半數為2.5,半數以上最少為3 2
6 6的半數為3,半數以上最少為4 2

通過以上可以發現,3臺伺服器和4臺伺服器都最多允許1臺伺服器掛掉,5臺伺服器和6臺伺服器都最多允許2臺伺服器掛掉,明顯4臺伺服器成本高於3臺伺服器成本,6臺伺服器成本高於5伺服器成本。這是由於半數以上投票通過決定的。

  1. 改變伺服器狀態
    一旦確定了 leader,伺服器就會更改自己的狀態,且一半不會再發生變化,比如新機器加入叢集、非 leader 掛掉一臺。

Follower :是 Zookeeper 叢集狀態的跟隨者。他的邏輯就比較簡單。除了響應本伺服器上的讀請求外,follower 還要處理leader 的提議,並在 leader 提交該提議時在本地也進行提交。另外需要注意的是,leader 和 follower 構成ZooKeeper 叢集的法定人數,也就是說,只有他們才參與新 leader的選舉、響應 leader 的提議。

Observer :伺服器充當一個觀察者的角色。如果 ZooKeeper 叢集的讀取負載很高,或者客戶端多到跨機房,可以設定一些 observer 伺服器,以提高讀取的吞吐量。Observer 和 Follower 比較相似,只有一些小區別:首先 observer 不屬於法定人數,即不參加選舉也不響應提議,也不參與寫操作的“過半寫成功”策略;其次是 observer 不需要將事務持久化到磁碟,一旦 observer 被重啟,需要從 leader 重新同步整個名字空間。

5.3會話(Session)

Session 指的是 ZooKeeper 伺服器與客戶端會話。在 ZooKeeper 中,一個客戶端連線是指客戶端和伺服器之間的一個 TCP 長連線。客戶端啟動的時候,首先會與伺服器建立一個 TCP 連線,從第一次連線建立開始,客戶端會話的生命週期也開始了。通過這個連線,客戶端能夠通過心跳檢測與伺服器保持有效的會話,也能夠向Zookeeper 伺服器傳送請求並接受響應,同時還能夠通過該連線接收來自伺服器的Watch事件通知。 Session 的 sessionTimeout 值用來設定一個客戶端會話的超時時間。當由於伺服器壓力太大、網路故障或是客戶端主動斷開連線等各種原因導致客戶端連線斷開時,只要在sessionTimeout規定的時間內能夠重新連線上叢集中任意一臺伺服器,那麼之前建立的會話仍然有效。在為客戶端建立會話之前,服務端首先會為每個客戶端都分配一個sessionID。由於 sessionID 是 Zookeeper 會話的一個重要標識,許多與會話相關的執行機制都是基於這個 sessionID 的,因此,無論是哪臺伺服器為客戶端分配的 sessionID,都務必保證全域性唯一。

5.3.1 會話(Session)

在Zookeeper客戶端與服務端成功完成連線建立後,就建立了一個會話,Zookeeper會話在整個執行期間的生命週期中,會在不同的會話狀態中之間進行切換,這些狀態可以分為CONNECTING、CONNECTED、RECONNECTING、RECONNECTED、CLOSE等。

一旦客戶端開始建立Zookeeper物件,那麼客戶端狀態就會變成CONNECTING狀態,同時客戶端開始嘗試連線服務端,連線成功後,客戶端狀態變為CONNECTED,通常情況下,由於斷網或其他原因,客戶端與服務端之間會出現斷開情況,一旦碰到這種情況,Zookeeper客戶端會自動進行重連服務,同時客戶端狀態再次變成CONNCTING,直到重新連上服務端後,狀態又變為CONNECTED,在通常情況下,客戶端的狀態總是介於CONNECTING 和CONNECTED 之間。但是,如果出現諸如會話超時、許可權檢查或是客戶端主動退出程式等情況,客戶端的狀態就會直接變更為CLOSE狀態。

5.3.2 會話建立

Session是Zookeeper中的會話實體,代表了一個客戶端會話,其包含了如下四個屬性

  1. sessionID。會話ID,唯一標識一個會話,每次客戶端建立新的會話時,Zookeeper都會為其分配一個全域性唯一的sessionID。
  2. TimeOut。會話超時時間,客戶端在構造Zookeeper例項時,會配置sessionTimeout引數用於指定會話的超時時間,Zookeeper客戶端向服務端傳送這個超時時間後,服務端會根據自己的超時時間限制最終確定會話的超時時間。
  3. TickTime。下次會話超時時間點,為了便於Zookeeper對會話實行”分桶策略”管理,同時為了高效低耗地實現會話的超時檢查與清理,Zookeeper會為每個會話標記一個下次會話超時時間點,其值大致等於當前時間加上TimeOut。
  4. isClosing。標記一個會話是否已經被關閉,當服務端檢測到會話已經超時失效時,會將該會話的isClosing標記為”已關閉”,這樣就能確保不再處理來自該會話的心情求了。

Zookeeper為了保證請求會話的全域性唯一性,在SessionTracker初始化時,呼叫initializeNextSession方法生成一個sessionID,之後在Zookeeper執行過程中,會在該sessionID的基礎上為每個會話進行分配,初始化演算法如下

    public static long initializeNextSession(long id) {
      long nextSid = 0;
      // 無符號右移8位使為了避免左移24後,再右移8位出現負數而無法通過高8位確定sid值
      nextSid = (System.currentTimeMillis() << 24) >>> 8;
      nextSid = nextSid | (id << 56);
      return nextSid;
    }

5.3.3 會話管理

Zookeeper的會話管理主要是通過SessionTracker來負責,其採用了分桶策略(將類似的會話放在同一區塊中進行管理)進行管理,以便Zookeeper對會話進行不同區塊的隔離處理以及同一區塊的統一處理。

分捅策略

5.4 資料節點 Znode

在Zookeeper中,“節點"分為兩類,第一類同樣是指構成叢集的機器,我們稱之為機器節點;第二類則是指資料模型中的資料單元,我們稱之為資料節點一一ZNode。
Zookeeper將所有資料儲存在記憶體中,資料模型是一棵樹(Znode Tree),由斜槓(/)的進行分割的路徑,就是一個Znode,例如/foo/path1。每個上都會儲存自己的資料內容,同時還會儲存一系列屬性資訊。

 

節點

5.4.1 節點型別

在Zookeeper中,node可以分為持久節點和臨時節點和順序節點三大類。
可以通過組合生成如下四種類型節點
1. PERSISTENT
持久節點,節點建立後便一直存在於Zookeeper伺服器上,直到有刪除操作來主動清楚該節點。
2. PERSISTENT_SEQUENTIAL
持久順序節點,相比持久節點,其新增了順序特性,每個父節點都會為它的第一級子節點維護一份順序,用於記錄每個子節點建立的先後順序。在建立節點時,會自動新增一個數字字尾,作為新的節點名,該數字字尾的上限是整形的最大值。
3.EPEMERAL
臨時節點,臨時節點的生命週期與客戶端會話繫結,客戶端失效,節點會被自動清理。同時,Zookeeper規定不能基於臨時節點來建立子節點,即臨時節點只能作為葉子節點。
4.EPEMERAL_SEQUENTIAL
臨時順序節點,在臨時節點的基礎添加了順序特性。

5.5 版本——保證分散式資料原子性操作

每個資料節點都具有三種類型的版本資訊,對資料節點的任何更新操作都會引起版本號的變化。

version– 當前資料節點資料內容的版本號
cversion– 當前資料子節點的版本號
aversion– 當前資料節點ACL變更版本號

上述各版本號都是表示修改次數,如version為1表示對資料節點的內容變更了一次。即使前後兩次變更並沒有改變資料內容,version的值仍然會改變。version可以用於寫入驗證,類似於CAS。

5.6watcher事件監聽器

ZooKeeper允許使用者在指定節點上註冊一些Watcher,當資料節點發生變化的時候,ZooKeeper伺服器會把這個變化的通知傳送給感興趣的客戶端

 

監聽

5.7 ACL 許可權控制——保障資料的安全

ACL是Access Control Lists 的簡寫, ZooKeeper採用ACL策略來進行許可權控制,有以下許可權:
CREATE:建立子節點的許可權
READ:獲取節點資料和子節點列表的許可權
WRITE:更新節點資料的許可權
DELETE:刪除子節點的許可權
ADMIN:設定節點ACL的許可權

5.8 Paxos演算法

Paxos演算法是基於訊息傳遞且具有高度容錯特性的一致性演算法,是目前公認的解決分散式一致性問題最有效的演算法之一。(其他演算法有二階段提交、三階段提交等)

六、ZooKeeper 可以做什麼?

  • 分散式服務註冊與訂閱

    在分散式環境中,為了保證高可用性,通常同一個應用或同一個服務的提供方都會部署多份,達到對等服務。而消費者就須要在這些對等的伺服器中選擇一個來執行相關的業務邏輯,比較典型的服務註冊與訂閱,代表:dubbo。

     

    註冊中心

  • 分散式配置中心
    釋出與訂閱模型,即所謂的配置中心,顧名思義就是釋出者將資料釋出到ZK節點上,供訂閱者獲取資料,實現配置資訊的集中式管理和動態更新。代表:百度的disconf。
    github:https://github.com/knightliao/disconf

  • 命名服務
    在分散式系統中,通過使用命名服務,客戶端應用能夠根據指定名字來獲取資源或服務的地址,提供者等資訊。被命名的實體通常可以是叢集中的機器,提供的服務地址,程序物件等等——這些我們都可以統稱他們為名字(Name)。其中較為常見的就是一些分散式服務框架中的服務地址列表。通過呼叫ZK提供的建立節點的API,能夠很容易建立一個全域性唯一的path,這個path就可以作為一個名稱。

    命名

     

  • 分散式鎖
    分散式鎖,這個主要得益於ZooKeeper為我們保證了資料的強一致性。鎖服務可以分為兩類,一個是保持獨佔,另一個是控制時序。
    所謂保持獨佔,就是所有試圖來獲取這個鎖的客戶端,最終只有一個可以成功獲得這把鎖。通常的做法是把zk上的一個znode看作是一把鎖,通過create znode的方式來實現。所有客戶端都去建立 /distribute_lock 節點,最終成功建立的那個客戶端也即擁有了這把鎖。
    控制時序,就是所有檢視來獲取這個鎖的客戶端,最終都是會被安排執行,只是有個全域性時序了。做法和上面基本類似,只是這裡 /distribute_lock 已絆預先存在,客戶端在它下面建立臨時有序節點(這個可以通過節點的屬性控制:CreateMode.EPHEMERAL_SEQUENTIAL來指定)。Zk的父節點(/distribute_lock)維持一份sequence,保證子節點建立的時序性,從而也形成了每個客戶端的全域性時序

  • Master選舉

  • 負載均衡

    負載均衡

     

參考

  • 《從Paxos到Zookeeper 》
  • 《ZooKeeper深入淺出 》

不知道大家是否還記的文章開頭的兩個問題,礙於本文篇幅的原因這裡不再一一解答,想要了解答案,想往架構師進發的小夥伴可以加群895244712一起交流學習,視訊我已經整理好放在群檔案了。

最後,如有幫助請轉發,點贊。您的拇指是對我最好的支援!