9張圖,Kafka為什么要放棄Zookeeper
最近,confluent社區(qū)發(fā)表了一篇文章,主要講述了Kafka未來的2.8版本將要放棄Zookeeper,這對于Kafka用戶來說,是一個重要的改進。之前部署Kafka就必須得部署Zookeeper,而之后就只要單獨部署Kafka就行了。[1]
1.Kafka簡介
Apache Kafka最早是由Linkedin公司開發(fā),后來捐獻給了Apack基金會。
Kafka被官方定義為分布式流式處理平臺,因為具備高吞吐、可持久化、可水平擴展等特性而被廣泛使用。目前Kafka具體如下功能:
- 消息隊列,Kafka具有系統(tǒng)解耦、流量削峰、緩沖、異步通信等消息隊列的功能。
- 分布式存儲系統(tǒng),Kafka可以把消息持久化,同時用多副本來實現(xiàn)故障轉(zhuǎn)移,可以作為數(shù)據(jù)存儲系統(tǒng)來使用。
- 實時數(shù)據(jù)處理,Kafka提供了一些和數(shù)據(jù)處理相關(guān)的組件,比如Kafka Streams、Kafka Connect,具備了實時數(shù)據(jù)的處理功能。
下面這張圖是Kafka的消息模型:[2]
通過上面這張圖,介紹一下Kafka中的幾個主要概念:
- producer和consumer: 消息隊列中的生產(chǎn)者和消費者,生產(chǎn)者將消息推送到隊列,消費者從隊列中拉取消息。
- consumer group:消費者集合,這些消費者可以并行消費同一個topic下不同partition中的消息。
- broker:Kafka集群中的服務(wù)器。
- topic:消息的分類。
- partition:topic物理上的分組,一個topic可以有partition,每個partition中的消息會被分配一個有序的id作為offset。每個consumer group只能有一個消費者來消費一個partition。
2.Kafka和Zookeeper關(guān)系
Kafka架構(gòu)如下圖:圖片從圖中可以看到,Kafka的工作需要Zookeeper的配合。那他們到底是怎么配合工作呢?
看下面這張圖:
從圖中可以看到,Kafka的工作需要Zookeeper的配合。那他們到底是怎么配合工作呢?
看下面這張圖:
2.1 注冊中心
2.1.1 broker注冊
從上面的圖中可以看到,broker分布式部署,就需要一個注冊中心來進行統(tǒng)一管理。Zookeeper用一個專門節(jié)點保存Broker服務(wù)列表,也就是 /brokers/ids。
broker在啟動時,向Zookeeper發(fā)送注冊請求,Zookeeper會在/brokers/ids下創(chuàng)建這個broker節(jié)點,如/brokers/ids/[0...N],并保存broker的IP地址和端口。
這個節(jié)點臨時節(jié)點,一旦broker宕機,這個臨時節(jié)點會被自動刪除。
2.1.2 topic注冊
Zookeeper也會為topic分配一個單獨節(jié)點,每個topic都會以/brokers/topics/[topic_name]的形式記錄在Zookeeper。
一個topic的消息會被保存到多個partition,這些partition跟broker的對應(yīng)關(guān)系也需要保存到Zookeeper。
partition是多副本保存的,上圖中紅色partition是leader副本。當leader副本所在的broker發(fā)生故障時,partition需要重新選舉leader,這個需要由Zookeeper主導完成。
broker啟動后,會把自己的Broker ID注冊到到對應(yīng)topic節(jié)點的分區(qū)列表中。
我們查看一個topic是xxx,分區(qū)編號是1的信息,命令如下:
- [root@master] get /brokers/topics/xxx/partitions/1/state
- {"controller_epoch":15,"leader":11,"version":1,"leader_epoch":2,"isr":[11,12,13]}
當broker退出后,Zookeeper會更新其對應(yīng)topic的分區(qū)列表。
2.1.3 consumer注冊
消費者組也會向Zookeeper進行注冊,Zookeeper會為其分配節(jié)點來保存相關(guān)數(shù)據(jù),節(jié)點路徑為/consumers/{group_id},有3個子節(jié)點,如下圖:
這樣Zookeeper可以記錄分區(qū)跟消費者的關(guān)系,以及分區(qū)的offset。[3]
2.2 負載均衡
broker向Zookeeper進行注冊后,生產(chǎn)者根據(jù)broker節(jié)點來感知broker服務(wù)列表變化,這樣可以實現(xiàn)動態(tài)負載均衡。
consumer group中的消費者,可以根據(jù)topic節(jié)點信息來拉取特定分區(qū)的消息,實現(xiàn)負載均衡。
實際上,Kafka在Zookeeper中保存的元數(shù)據(jù)非常多,看下面這張圖:
隨著broker、topic和partition增多,保存的數(shù)據(jù)量會越來越大。
3.Controller介紹
經(jīng)過上一節(jié)的講述,我們看到了Kafka對Zookeeper的依賴非常大,Kafka離開Zookeeper是沒有辦法獨立運行的。那Kafka是怎么跟Zookeeper進行交互的呢?
如下圖:[4]圖片Kafka集群中會有一個broker被選舉為Controller負責跟Zookeeper進行交互,它負責管理整個Kafka集群中所有分區(qū)和副本的狀態(tài)。其他broker監(jiān)聽Controller節(jié)點的數(shù)據(jù)變化。
Controller的選舉工作依賴于Zookeeper,選舉成功后,Zookeeper會創(chuàng)建一個/controller臨時節(jié)點。
Controller具體職責如下:
- 監(jiān)聽分區(qū)變化
比如當某個分區(qū)的leader出現(xiàn)故障時,Controller會為該分區(qū)選舉新的leader。當檢測到分區(qū)的ISR集合發(fā)生變化時,Controller會通知所有broker更新元數(shù)據(jù)。當某個topic增加分區(qū)時,Controller會負責重新分配分區(qū)。
- 監(jiān)聽topic相關(guān)的變化
- 監(jiān)聽broker相關(guān)的變化
- 集群元數(shù)據(jù)管理
下面這張圖展示了Controller、Zookeeper和broker的交互細節(jié):
Controller選舉成功后,會從Zookeeper集群中拉取一份完整的元數(shù)據(jù)初始化ControllerContext,這些元數(shù)據(jù)緩存在Controller節(jié)點。當集群發(fā)生變化時,比如增加topic分區(qū),Controller不僅需要變更本地的緩存數(shù)據(jù),還需要將這些變更信息同步到其他Broker。
Controller監(jiān)聽到Zookeeper事件、定時任務(wù)事件和其他事件后,將這些事件按照先后順序暫存到LinkedBlockingQueue中,由事件處理線程按順序處理,這些處理多數(shù)需要跟Zookeeper交互,Controller則需要更新自己的元數(shù)據(jù)。
4.Zookeeper帶來的問題
Kafka本身就是一個分布式系統(tǒng),但是需要另一個分布式系統(tǒng)來管理,復雜性無疑增加了。
4.1 運維復雜度
使用了Zookeeper,部署Kafka的時候必須要部署兩套系統(tǒng),Kafka的運維人員必須要具備Zookeeper的運維能力。
4.2 Controller故障處理
Kafaka依賴一個單一Controller節(jié)點跟Zookeeper進行交互,如果這個Controller節(jié)點發(fā)生了故障,就需要從broker中選擇新的Controller。如下圖,新的Controller變成了broker3。
新的Controller選舉成功后,會重新從Zookeeper拉取元數(shù)據(jù)進行初始化,并且需要通知其他所有的broker更新ActiveControllerId。老的Controller需要關(guān)閉監(jiān)聽、事件處理線程和定時任務(wù)。分區(qū)數(shù)非常多時,這個過程非常耗時,而且這個過程中Kafka集群是不能工作的。
4.3 分區(qū)瓶頸
當分區(qū)數(shù)增加時,Zookeeper保存的元數(shù)據(jù)變多,Zookeeper集群壓力變大,達到一定級別后,監(jiān)聽延遲增加,給Kafaka的工作帶來了影響。
所以,Kafka單集群承載的分區(qū)數(shù)量是一個瓶頸。而這又恰恰是一些業(yè)務(wù)場景需要的。
5.升級
升級前后的架構(gòu)圖對比如下:
KIP-500用Quorum Controller代替之前的Controller,Quorum中每個Controller節(jié)點都會保存所有元數(shù)據(jù),通過KRaft協(xié)議保證副本的一致性。這樣即使Quorum Controller節(jié)點出故障了,新的Controller遷移也會非???。
官方介紹,升級之后,Kafka可以輕松支持百萬級別的分區(qū)。
Kafak團隊把通過Raft協(xié)議同步數(shù)據(jù)的方式Kafka Raft Metadata mode,簡稱KRaft
Kafka的用戶體量非常大,在不停服的情況下升級是必要的。
目前去除Zookeeper的Kafka代碼KIP-500已經(jīng)提交到trunk分支,并且計劃在未來的2.8版本發(fā)布。
Kafaka計劃在3.0版本會兼容Zookeeper Controller和Quorum Controller,這樣用戶可以進行灰度測試。[5]
6.總結(jié)
在大規(guī)模集群和云原生的背景下,使用Zookeeper給Kafka的運維和集群性能造成了很大的壓力。去除Zookeeper是必然趨勢,這也符合大道至簡的架構(gòu)思想。
Reference
[1]參考1:
https://www.confluent.io/blog/kafka-without-zookeeper-a-sneak-peek/
[2]參考2:
https://blog.csdn.net/Zidingyi_367/article/details/110490910
[3]參考3:
https://www.jianshu.com/p/a036405f989c
[4]參考4:
https://honeypps.com/mq/kafka-controller-analysis/
[5]參考5:
https://mp.weixin.qq.com/s/ev6NM6hptltQBuTaCHJCQQ