一、ZooKeeper 簡介
1.基本介紹
ZooKeeper 的官網是:https://zookeeper.apache.org/。在官網上是這麼介紹 ZooKeeper 的:ZooKeeper 是一項集中式服務,用於維護配置資訊,命名,提供分散式同步和提供組服務。
當我們編寫程式的時候,通常會將所有的配置資訊儲存在一個配置檔案中,例如賬號、密碼等資訊,後續直接修改配置檔案就行了,那分散式場景下如何配置呢?如果說每臺機器上都儲存一個配置檔案,這時候要一臺臺的去修改配置檔案難免出錯,而且要管理這些機器也會變得複雜和困難,ZooKeeper 的出現就是為了解決這類問題,實現高度可靠的分散式系統。
2.基本功能
1)配置管理
ZooKeeper 為分散式系統提供了一種配置管理的服務:集中管理配置,即將全域性配置資訊儲存在 ZooKeeper 服務中,方便進行修改和管理,省去了手動拷貝配置的過程,同時還保證了可靠和一致性。
2)命名服務
在分散式系統中,經常需要對應用或者服務進行統一命名,便於識別和區分開來,而 ZooKeeper 就提供了這種服務。
3)分散式鎖
鎖應該都不陌生,沒有用過也聽說過,在多個程式訪問互斥資源的時候,需要加上一道鎖。在分散式系統中,分散式程式分佈在各個主機上的程式對互斥資源進行訪問時也需要加鎖。
分散式鎖應當具備以下條件:
- 在分散式系統環境下,一個方法在同一時間只能被一個機器的一個執行緒執行;
- 高可用的獲取鎖與釋放鎖;
- 高效能的獲取鎖與釋放鎖;
- 具備可重入特性(可理解為重新進入,由多於一個任務併發使用,而不必擔心資料錯誤);
- 具備鎖失效機制,防止死鎖;
- 具備非阻塞鎖特性,即沒有獲取到鎖將直接返回獲取鎖失敗。
4)叢集管理
在分散式系統中,由於各種各樣的原因,例如機器故障、網路故障等,導致叢集中的節點增加或者減少,叢集中有些機器需要感知到這種變化,然後根據這種變化做出對應的決策。
3.基本架構
ZooKeeper 的基本架構圖如下:
我們需要知道以下幾點:
- Client 表示客戶端,是請求傳送方,數量不限;
- Server 表示服務端,是請求接收方,數量不限;
- Client 可以連線到每個 Server,每個 Server 中的資料都是一樣的;
- ZooKeeper 啟動時,會從所有 Server 中選取一個作為 leader(Paxos 協議),每個 follower 都會和 leader 建立連線;
- leader 負責進行資料更新等操作,並將資料同步到 follower 中,以此實現資料一致性。
4.ZooKeeper節點
我們使用 znode 來明確表示 ZooKeeper 的資料節點。下圖表示的是 ZooKeeper 的命名層次空間,名稱是由斜槓(/)分隔的一系列路徑元素。
znode 有四種型別 :
1)PERSISTENT(持久節點)
預設的節點型別。持久化儲存的節點,建立節點的客戶端與 ZooKeeper 斷開連線後,該節點依舊存在 。
2)PERSISTENT_SEQUENTIAL(持久順序節點)
所謂順序節點,就是在建立節點時,ZooKeeper 根據建立的時間順序給該節點名稱進行編號,適合用於分散式鎖、分散式選舉等場景。建立時新增 -s 引數即可。
3)EPHEMERAL(臨時節點)
和持久節點相反,當建立節點的客戶端與 ZooKeeper 斷開連線後,臨時節點會自動刪除,適用於心跳、服務發現等場景。建立時新增引數-e 即可。
4)EPHEMERAL_SEQUENTIAL(臨時順序節點)
顧名思義,該類節點結合了臨時節點和順序節點的特徵,在建立節點時,ZooKeeper 根據建立的時間順序給該節點名稱進行編號,當建立節點的客戶端和 ZooKeeper 斷開連線後,節點自動刪除。建立時新增 -e -s 引數即可。
ZooKeeper 實現分散式鎖就是利用了臨時順序節點。
首先在 ZooKeeper 中建立一個持久節點 ParentLock,每當有客戶端想要獲得鎖時,就在 ParentLock 下建立一個臨時順序節點,如果該節點是第一個,則獲得鎖,如果不是,則找到排序比它靠前的一個節點並註冊 Watcher,用於監聽節點是否存在。若第一個節點執行完畢,其客戶端會呼叫指令刪除該節點,或者其客戶端崩潰,第一個節點也會自動刪除,而第二個節點監聽到該節點被刪除,再經過查詢確認就能獲得鎖了。不難發現 ZooKeeper 實現的分散式鎖相當於是一個等待鎖的佇列,能夠提升搶鎖的效率,但是因為需要建立和刪除節點,導致效能較低。
二、Kafka + ZooKeeper
ZooKeeper 作為給分散式系統提供協調服務的工具被 kafka 所依賴。在分散式系統中,消費者需要知道有哪些生產者是可用的,而如果每次消費者都需要和生產者建立連線並測試是否成功連線,那效率也太低了,顯然是不可取的。而通過使用 ZooKeeper 協調服務,Kafka 就能將 Producer,Consumer,Broker 等結合在一起,同時藉助 ZooKeeper,Kafka 就能夠將所有元件在無狀態的條件下建立起生產者和消費者的訂閱關係,實現負載均衡。
1)Broker 資訊
在 ZooKeeper 上會有一個專門用來進行 Broker 伺服器列表記錄的節點,節點路徑為 /brokers/ids。Kafka 的每個 Broker 啟動時,都會在 ZooKeeper 中註冊,建立 /brokers/ids/[0-N] 節點,寫入 IP,埠等資訊,每個 Broker 都有一個 BrokerId。Broker 建立的是臨時節點,在連線斷開時節點就會自動刪除,所以在 ZooKeeper 上就可以通過 Broker 中節點的變化來得到 Broker 的可用性。
2)Topic 資訊
在 Kafka 中可以定義很多個 Topic,每個 Topic 又被分為很多個 Partition。一般情況下,每個 Partition 獨立在存在一個 Broker 上,所有的這些 Topic 和 Broker 的對應關係都由 ZooKeeper 進行維護。
3)負載均衡
生產者需要將訊息傳送給 Broker,消費者需要從 Broker 上獲取訊息,通過使用 ZooKeeper,就都能監聽 Broker 上節點的狀態資訊,從而實現動態負載均衡。
4)offset 資訊
在上一篇部落格中提到過,offset 用於記錄消費者消費到的位置,在老版本(0.9以前)裡 offset 是儲存在 ZooKeeper 中的。
5)Controller 選舉
在 Kafka 中會有多個 Broker,其中一個 Broker 會被選舉成為 Controller(控制器),在任意時刻,Kafka 叢集中有且僅有一個控制器。Controller 負責管理叢集中所有分割槽和副本的狀態,當某個分割槽的 leader 副本出現故障時,由 Controller 為該分割槽選舉出一個新的 leader。Kafka 的 Controller 選舉就依靠 ZooKeeper 來完成,成功競選為 Controller 的 Broker 會在 ZooKeeper 中建立 /controller 這個臨時節點,在 ZooKeeper 中使用 get 命令檢視節點內容:
其中“version”在目前版本中固定為1,“brokerid”表示 Broker 的編號,“timestamp”表示競選稱為 Controller 時的時間戳。
當 Broker 啟動時,會嘗試讀取 /controller 中的“brokerid ”,如果讀取到的值不是-1,則表示已經有節點競選成為 Controller 了,當前節點就會放棄競選;而如果讀取到的值為-1,ZooKeeper 就會嘗試建立 /controller 節點,當該 Broker 去建立的時候,可能還有其他 Broker 一起同時建立節點,但只有一個 Broker 能夠建立成功,即成為唯一的 Controller。