【精華】圖解Kafka入門

在這裏插入圖片描述

  要做一個消息隊列可能要考慮到以下的問題:

  • 使用消息隊列不可能是單機的(必然是分佈式or集羣)?
  • 數據寫到消息隊列,可能會存在數據丟失問題,數據在消息隊列需要持久化(磁盤?數據庫?Redis?分佈式文件系統?)
  • 想要保證消息(數據)是有序的,怎麼做?
  • 爲什麼在消息隊列中重複消費了數據?

  Kafka是一個消息隊列,把消息放到隊列裏邊的叫生產者,從隊列裏邊消費的叫消費者。
在這裏插入圖片描述
  一個消息中間件,隊列不單單隻有一個,我們往往會有多個隊列,而我們生產者和消費者就得知道:把數據丟給哪個隊列,從哪個隊列取消息。我們需要給隊列取名字,叫做topic(相當於數據庫裏邊表的概念)。
在這裏插入圖片描述
  現在我們給隊列取了名字以後,生產者就知道往哪個隊列丟數據了,消費者也知道往哪個隊列拿數據了。我們可以有多個生產者往同一個隊列(topic)丟數據,多個消費者往同一個隊列(topic)拿數據。
在這裏插入圖片描述
  爲了提高一個隊列(topic)的吞吐量,Kafka會把topic進行分區(Partition)。
在這裏插入圖片描述
  所以,生產者實際上是往一個topic名爲Java3y中的分區(Partition)丟數據,消費者實際上是往一個topic名爲Java3y的分區(Partition)取數據。
在這裏插入圖片描述
  一臺Kafka服務器叫做Broker,Kafka集羣就是多臺Kafka服務器。
在這裏插入圖片描述
  一個topic會分爲多個partition,實際上partition會分佈在不同的broker中,舉個例子:
在這裏插入圖片描述
  由此得知:Kafka是天然分佈式的

  現在我們已經知道了往topic裏邊丟數據,實際上這些數據會分到不同的partition上,這些partition存在不同的broker上。分佈式肯定會帶來問題:“萬一其中一臺broker(Kafka服務器)出現網絡抖動或者掛了,怎麼辦?”

  Kafka是這樣做的:我們數據存在不同的partition上,那kafka就把這些partition做備份。比如,現在我們有三個partition,分別存在三臺broker上。每個partition都會備份,這些備份散落在不同的broker上。
在這裏插入圖片描述
  紅色塊的partition代表的是主分區,紫色的partition塊代表的是備份分區。生產者往topic丟數據,是與主分區交互,消費者消費topic的數據,也是與主分區交互。

  備份分區僅僅用作於備份,不做讀寫。如果某個Broker掛了,那就會選舉出其他Broker的partition來作爲主分區,這就實現了高可用。

  另外值得一提的是:當生產者把數據丟進topic時,我們知道是寫在partition上的,那partition是怎麼將其持久化的呢?(不持久化如果Broker中途掛了,那肯定會丟數據嘛)。

  Kafka是將partition的數據寫在磁盤的(消息日誌),不過Kafka只允許追加寫入(順序訪問),避免緩慢的隨機 I/O 操作。

  Kafka也不是partition一有數據就立馬將數據寫到磁盤上,它會先緩存一部分,等到足夠多數據量或等待一定的時間再批量寫入(flush)。

  既然數據是保存在partition中的,那麼消費者實際上也是從partition中取數據。
在這裏插入圖片描述
  生產者可以有多個,消費者也可以有多個。像上面圖的情況,是一個消費者消費三個分區的數據。多個消費者可以組成一個消費者組。本來是一個消費者消費三個分區的,現在我們有消費者組,就可以每個消費者去消費一個分區(也是爲了提高吞吐量)。
在這裏插入圖片描述
  按圖上所示的情況,這裏想要說明的是:

  • 如果消費者組中的某個消費者掛了,那麼其中一個消費者可能就要消費兩個partition了
  • 如果只有三個partition,而消費者組有4個消費者,那麼一個消費者會空閒
  • 如果多加入一個消費者組,無論是新增的消費者組還是原本的消費者組,都能消費topic的全部數據。(消費者組之間從邏輯上它們是獨立的)

  前面講解到了生產者往topic裏丟數據是存在partition上的,而partition持久化到磁盤是IO順序訪問的,並且是先寫緩存,隔一段時間或者數據量足夠大的時候才批量寫入磁盤的。
  消費者在讀的時候也很有講究:正常的讀磁盤數據是需要將內核態數據拷貝到用戶態的,而Kafka 通過調用sendfile()直接從內核空間(DMA的)到內核空間(Socket的),少做了一步拷貝的操作。

在這裏插入圖片描述
  如果一個消費者組中的某個消費者掛了,那掛掉的消費者所消費的分區可能就由存活的消費者消費。那存活的消費者是需要知道掛掉的消費者消費到哪了

  Kafka用offset來表示消費者的消費進度到哪了,即offset就是表示消費者的消費進度,每個消費者會都有自己的offset。

  在以前版本的Kafka,這個offset是由Zookeeper來管理的,後來Kafka開發者認爲Zookeeper不合適大量的刪改操作,於是把offset在broker以內部topic(__consumer_offsets)的方式來保存起來。

  每次消費者消費的時候,都會提交這個offset,Kafka可以讓你選擇是自動提交還是手動提交。

  Zookeeper雖然在新版的Kafka中沒有用作於保存客戶端的offset,但是Zookeeper是Kafka一個重要的依賴。

  • 探測broker和consumer的添加或移除。
  • 負責維護所有partition的領導者/從屬者關係(主分區和備份分區),如果主分區掛了,需要選舉出備份分區作爲主分區。
  • 維護topic、partition等元配置信息。
  • ……
    在這裏插入圖片描述

  最後解答一下開篇的幾個問題。

1.使用消息隊列不可能是單機的(必然是分佈式or集羣)
  Kafka天然是分佈式的,往一個topic丟數據,實際上就是往多個broker的partition存儲數據。

2.數據寫到消息隊列,可能會存在數據丟失問題,數據在消息隊列需要持久化(磁盤?數據庫?Redis?分佈式文件系統?)
  Kafka會將partition以消息日誌的方式(落磁盤)存儲起來,通過 順序訪問IO和緩存(等到一定的量或時間)才真正把數據寫到磁盤上,來提高速度。

3.想要保證消息(數據)是有序的,怎麼做?
  Kafka會將數據寫到partition,單個partition的寫入是有順序的。如果要保證全局有序,那隻能寫入一個partition中。如果要消費也有序,消費者也只能有一個。

4.爲什麼在消息隊列中重複消費了數據?
  凡是分佈式就無法避免網絡抖動/機器宕機等問題的發生,很有可能消費者A讀取了數據,還沒來得及消費,就掛掉了。Zookeeper發現消費者A掛了,讓消費者B去消費原本消費者A的分區,等消費者A重連的時候,發現已經重複消費同一條數據了。(各種各樣的情況,消費者超時等等都有可能…)
  如果業務上不允許重複消費的問題,最好消費者那端做業務上的校驗(如果已經消費過了,就不消費了)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章