Kafka元數據緩存(metadata cache)


  經常有人問的一個問題就是:Kafka broker到底是不是無狀態的?網上有這樣的說法:

正常情況下consumer會在消費完一條消息后線性增加這個offset。當然,consumer也可將offset設成一個較小的值,重新消費一些消息。因為offet由consumer控制,所以Kafka broker是無狀態的。。。。。。

  我猜想作者的意思應該是說:broker不保存消費者的狀態。如果從這個角度來說,broker無狀態的說法倒也沒有什么問題。不過實際上,broker是有狀態的服務:每台broker在內存中都維護了集群上所有節點和topic分區的狀態信息——Kafka稱這部分狀態信息為元數據緩存(metadata cache)。本文就將討論一下這個metadata cache的設計與實現。

1. cache里面存了什么?

  首先,我們來看下cache里面都存了什么,我們以Kafka 1.0.0版本作為分析對象。Metadata cache中保存的信息十分豐富,幾乎囊括了Kafka集群的各個方面,它包含了:

  • controller所在的broker ID,即保存了當前集群中controller是哪台broker
  • 集群中所有broker的信息:比如每台broker的ID、機架信息以及配置的若干組連接信息(比如配置了PLAINTEXT和SASL監聽器就有兩套連接信息,分別使用不同的安全協議和端口,甚至主機名都可能不同)
  • 集群中所有節點的信息:嚴格來說,它和上一個有些重復,不過此項是按照broker ID和監聽器類型進行分組的。對於超大集群來說,使用這一項緩存可以快速地定位和查找給定節點信息,而無需遍歷上一項中的內容,算是一個優化吧
  • 集群中所有分區的信息:所謂分區信息指的是分區的leader、ISR和AR信息以及當前處於offline狀態的副本集合。這部分數據按照topic和分區ID進行分組,可以快速地查找到每個分區的當前狀態。(注:AR表示assigned replicas,即創建topic時為該分區分配的副本集合)

2. 每台broker都保存相同的cache嗎?

  是的,至少Kafka在設計時的確是這樣的願景:每台Kafka broker都要維護相同的緩存,這樣客戶端程序(clients)隨意地給任何一個broker發送請求都能夠獲取相同的數據,這也是為什么任何一個broker都能處理clients發來的Metadata請求的原因:因為每個broker上都有這些數據!要知道目前Kafka共有38種請求類型,能做到這一點的可謂少之又少。每個broker都能處理的能力可以縮短請求被處理的延時從而提高整體clients端的吞吐,因此用空間去換一些時間的做法是值得的。

3. cache是怎么更新的?

  如前所述,用空間去換時間,好處是降低了延時,提升了吞吐,但劣勢就在於你需要處理cache的更新並且維護一致性。目前Kafka是怎么更新cache的?簡單來說,就是通過發送異步更新請求(UpdateMetadata request)來維護一致性的。既然是異步的,那么在某一個時間點集群上所有broker的cache信息就未必是嚴格相同的。只不過在實際使用場景中,這種弱一致性似乎並沒有太大的問題。原因如下:1. clients並不是時刻都需要去請求元數據的,且會緩存到本地;2. 即使獲取的元數據無效或者過期了,clients通常都有重試機制,可以去其他broker上再次獲取元數據; 3. cache更新是很輕量級的,僅僅是更新一些內存中的數據結構,不會有太大的成本。因此我們還是可以安全地認為每台broker上都有相同的cache信息。

  具體的更新操作實際上是由controller來完成的。controller會在一定場景下向特定broker發送UpdateMetadata請求令這些broker去更新它們各自的cache,這些broker一旦接收到請求便開始全量更新——即清空當前所有cache信息,使用UpdateMetadata請求中的數據來重新填充cache。

4. cache什么時候更新?

  實際上這個問題等同於:controller何時向特定broker發送UpdateMetadata請求? 如果從源碼開始分析,那么涉及到的場景太多了,比如controller啟動時、新broker啟動時、更新broker時、副本重分配時等等。我們只需要記住:只要集群中有broker或分區數據發生了變更就需要更新這些cache。

  舉個經常有人問的例子:集群中新增加的broker是如何獲取這些cache,並且其他broker是如何知曉它的?當有新broker啟動時,它會在Zookeeper中進行注冊,此時監聽Zookeeper的controller就會立即感知這台新broker的加入,此時controller會更新它自己的緩存(注意:這是controller自己的緩存,不是本文討論的metadata cache)把這台broker加入到當前broker列表中,之后它會發送UpdateMetadata請求給集群中所有的broker(也包括那台新加入的broker)讓它們去更新metadata cache。一旦這些broker更新cache完成,它們就知道了這台新broker的存在,同時由於新broker也更新了cache,故現在它也有了集群所有的狀態信息。

5. 目前的問題?

  前面說過了,現在更新cache完全由controller來驅動,故controller所在broker的負載會極大地影響這部分操作(實際上,它會影響所有的controller操作)。根據目前的設計,controller所在broker依然作為一個普通broker執行其他的clients請求處理邏輯,所以如果controller broker一旦忙於各種clients請求(比如生產消息或消費消息),那么這種更新操作的請求就會積壓起來(backlog),造成了更新操作的延緩甚至是被取消。究其根本原因在於當前controller對待數據類請求和控制類請求並無任何優先級化處理——controller一視同仁地對待這些請求,而實際上我們更希望controller能否賦予控制類請求更高的優先級。社區目前已經開始着手改造當前的設計,相信在未來的版本中此問題可以得到解決。

 

  本文探討了一些關於metadata cache方面的內容,因為時間有限,並沒有涵蓋方方面面。不過對於我們了解cache的工作原理應該可以還是有幫助的~~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM