Zookeeper 基礎原理 - 應用場景詳解

簡單瞭解 Zookeeper

Tips: 如果之前對 Zookeeper 不瞭解的話,這裏大概留個印象就好了

Zookeeper 是一個分佈式協調服務,可以用於元數據管理、分佈式鎖、分佈式協調、發佈訂閱、服務命名等等。

例如,Kafka 中就是用 Zookeeper 來保存其集羣中的相關元數據,例如 Broker、Topic 以及 Partition 等等。同時,基於 Zookeeper 的 Watch 監聽機制,還可以用其實現發佈、訂閱的功能。

在平常的常規業務使用場景下,我們幾乎只會使用到分佈式鎖這一個用途。

Zookeeper 內部運行機制

Zookeeper 的底層存儲原理,有點類似於 Linux 中的文件系統。Zookeeper 中的文件系統中的每個文件都是節點(Znode)。根據文件之間的層級關係,Zookeeper 內部就會形成這個這樣一個文件樹。

在 Linux 中,文件(節點)其實是分類型的,例如分爲文件、目錄。在 Zookeeper 中同理,Znode 同樣的有類型。在 Zookeeper 中,所有的節點類型如下:

所謂持久節點,就和我們自己在電腦上新建一個文件一樣,除非你主動刪除,否則一直存在。

持久順序節點除了繼承了持久節點的特性之外,還會爲其下創建的子節點保證其先後順序,並且會自動地爲節點加上 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註冊一個監聽器,對於該節點的任意更新都將觸發對應的操作。

當其被刪除之後,就會喚醒客戶端 B 的線程,此時客戶端 B 會再次進行判斷自己是否是序號最小的一個節點,此時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 使用示例。

命名服務

用大白話來說,命名服務主要有兩種。

前者可以用於在系統之間共享某種業務上的特定資源,後者則可以用於實現分佈式鎖。

好了以上就是本篇博客的全部內容了,如果文章對你有幫助,還麻煩幫忙點一下文末的在看

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/jCmS6VvlFmjm2RWgqlpUqg