Zookeeper 基礎原理 - 應用場景詳解
簡單瞭解 Zookeeper
Tips: 如果之前對 Zookeeper 不瞭解的話,這裏大概留個印象就好了
Zookeeper 是一個分佈式協調服務,可以用於元數據管理、分佈式鎖、分佈式協調、發佈訂閱、服務命名等等。
例如,Kafka 中就是用 Zookeeper 來保存其集羣中的相關元數據,例如 Broker、Topic 以及 Partition 等等。同時,基於 Zookeeper 的 Watch 監聽機制,還可以用其實現發佈、訂閱的功能。
在平常的常規業務使用場景下,我們幾乎只會使用到分佈式鎖這一個用途。
Zookeeper 內部運行機制
Zookeeper 的底層存儲原理,有點類似於 Linux 中的文件系統。Zookeeper 中的文件系統中的每個文件都是節點(Znode)。根據文件之間的層級關係,Zookeeper 內部就會形成這個這樣一個文件樹。
在 Linux 中,文件(節點)其實是分類型的,例如分爲文件、目錄。在 Zookeeper 中同理,Znode 同樣的有類型。在 Zookeeper 中,所有的節點類型如下:
-
持久節點(Persistent)
-
持久順序節點(Persistent Sequential)
-
臨時節點(Ephemeral)
-
臨時順序節點(Ephemeral Sequential)
所謂持久節點,就和我們自己在電腦上新建一個文件一樣,除非你主動刪除,否則一直存在。
而持久順序節點除了繼承了持久節點的特性之外,還會爲其下創建的子節點保證其先後順序,並且會自動地爲節點加上 10 位自增序列號作爲節點名,以此來保證節點名的唯一性。這一點上圖中的subfiles
已經給出了示例。
而臨時節點,其生命週期和 client 的連接是否活躍相關,如果 client 一旦斷開連接,該節點(可以理解爲文件)就都會被刪除,並且臨時節點無法創建子節點;
PS:這裏的斷開連接其實不是我們直覺上理解的斷開連接,Zookeeper 有其 Session 機制,當某個 client 的 Session 過期之後,會將對應的 client 創建的節點全部刪除
Zookeeper 的節點創建方式
接下來我們來分別看看幾種節點的創建方式,給出幾個簡單的示例。
創建持久節點
create /node_name SH的全棧筆記
這裏需要注意的是,命令中所有的節點名稱必須要以/
開頭,否則會創建失敗,因爲在 Zookeeper 中是不能使用相對路徑,必須要使用絕對路徑。
創建持久順序節點
create -s /node_name SH的全棧筆記
可以看到,Zookeeper 爲 key 自動的加上了 10 位的自增後綴。
創建臨時節點
create -e /test SH的全棧筆記
創建臨時順序節點
create -e -s /node_name SH的全棧筆記
Zookeeper 的用途
我們通過一些具體的例子,來了解 Zookeeper 的詳細用途,它不僅僅只是被當作分佈式鎖使用。
元數據管理
我們都知道,Kafka 在運行時會依賴一個 Zookeeper 的集羣。Kafka 通過 Zookeeper 來管理集羣的相關元數據,並通過 Zookeeper 進行 Leader 選舉。
Tips: 但是即將發佈的 Kafka 2.8 版本中,Zookeeper 已經不是一個必需的組件了。這塊我暫時還沒有時間去細看,不過我估計可能會跟 RocketMQ 中處理的方式差不多,將其集羣的元數據放到 Kafka 本身來處理。
分佈式鎖
基於 Zookeeper 的分佈式鎖其實流程很簡單。首先我們需要知道加分佈式鎖的本質是什麼?
答案是創建臨時順序節點
當某個客戶端加鎖
成功之後,實際上則是成功的在 Zookeeper 上創建了臨時順序節點。我們知道,分佈式鎖能夠使同一時間只能有一個能夠訪問某種資源。那這就必然會涉及到分佈式鎖的競爭,那問題來了,當前這個客戶端是如何感知搶到了鎖呢?
其實在客戶端側會有一定的邏輯,假設加鎖的 key 爲/locks/modify_users
。
首先,客戶端會發起加鎖請求,然後會在 Zookeeper 上創建持久節點locks
,然後會在該節點下創建臨時順序節點。臨時順序節點的創建示例,如下圖所示。
當客戶端成功創建了節點之後,還會獲取其同級的所有節點。也就是上圖中的所有modify_users000000000x
的節點。
此時客戶端會根據 10 位的自增序號去判斷,當前自己創建的節點是否是所有的節點中最小的那個,如果是最小的則自己獲取到了分佈式鎖。
你可能會問,那如果我不是最小的怎麼辦呢?而且我的節點都已經創建了。如果不是最小的,說明當前客戶端並沒有搶到鎖。按照我們的認知,如果沒有競爭到分佈式鎖,則會等待。等待的底層都做了什麼?我們用實際例子來捋一遍。
假設 Zookeeper 中已經有了如下的節點。
例如當前客戶端是 B 創建的節點是modify_users0000000002
,那麼很明顯 B 沒有搶到鎖,因爲已經有比它還要小的由客戶端 A 創建的節點modify_users0000000001
。
此時客戶端 B 會對節點modify_users0000000001
註冊一個監聽器,對於該節點的任意更新都將觸發對應的操作。
modify_users0000000002
明顯是最小的節點,故客戶端 B 加鎖成功。
爲了讓你更加直觀的瞭解這個過程,我把流程濃縮成了下面這幅流程圖。
分佈式協調
我們都知道,在很多場景下要保證一致性都會採用經典的 2PC(兩階段提交),例如 MySQL 中 Redo Log 和 Binlog 提交的數據一致性保障就是採用的 2PC,詳情可以看基於 Redo Log 和 Undo Log 的 MySQL 崩潰恢復流程。
在 2PC 中存在兩種角色,分別是參與者(Participant)和協調者(Coordinator),協調者負責統一的調度所有分佈式節點的執行邏輯。具體協調啥呢?舉個例子。
例如在 2PC 的 Commit 階段,兩個參與者 A、B,A 的 commit 操作成功了,但不幸的是 B 失敗了。此時協調者就需要向 A 發送 Rollback 操作。Zookeeper 大概就是這樣一個角色。
發佈訂閱
由於 Zookeeper 自帶了監聽器(Watch)的功能,所以發佈訂閱也順理成章的成爲了 Zookeeper 的應用之一。例如在某個配置節點上註冊了監聽器,那麼該配置一旦發佈變更,對應的服務就能實時的感知到配置更改,從而達到配置的動態更新的目的。
給個簡單的 Watch 使用示例。
命名服務
用大白話來說,命名服務主要有兩種。
-
單純的利用 Zookeeper 的文件系統特性,存儲結構化的文件
-
利用文件特性和順序節點的特性,來生成全局的唯一標識
前者可以用於在系統之間共享某種業務上的特定資源,後者則可以用於實現分佈式鎖。
好了以上就是本篇博客的全部內容了,如果文章對你有幫助,還麻煩幫忙點一下文末的在看~
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/jCmS6VvlFmjm2RWgqlpUqg