Kafka 裏面的信息是如何被消費的?

作爲一個爬蟲工程師,Kafka 對你而言就是一個消息隊列,你只需要掌握如何向裏面寫入數據,以及如何讀取數據就可以了。

請謹記:使用 Kafka 很容易,但對 Kafka 集羣進行搭建、維護與調優很麻煩。Kafka 集羣需要有專人來維護,不要以爲你能輕易勝任這個工作。

本文,以及接下來的幾篇針對 Kafka 的文章,我們面向的對象都是爬蟲工程師或者僅僅需要使用 Kafka 的讀者。關於 Kafka 更深入的底層細節與核心原理,不在我們的討論範圍中。爲了解釋方便,文章中對 Kafka 的一些術語會使用一些不太準確但能表明意思的類比。如果你需要在面試中解釋這些術語,還請閱讀Kafka 的官方文檔。

今天我們要討論的一個話題是,Kafka 是如何做到,對單個程序的多個進程而言,能持續消費,斷點續傳和並行消費;對多個程序而言又互不影響,各自獨立。

一個 Kafka 可以有多個不同的隊列,我們把這個隊列叫做Topic,假設其中一個隊列如下圖所示:

信息從右邊進去,從左邊出來。如果這是Redis 的列表,那麼它彈出一條信息以後,隊列會變成下面這樣:

最左邊的信息1不見了。所以即使程序在消費了信息1後立刻關閉,再重新打開,程序也會接着從信息2開始消費,不會把信息1重複消費兩次。

但我如果有兩個程序呢?程序1讀取每一條數據,再轉存到數據庫。程序2讀取每一條數據,再檢查是否有關鍵詞。這種情況下,信息1應該能被程序1消費,也能被程序2消費。但上面這種方案顯然是不行的。當程序1消費了信息1,程序2就再也拿不到它了。

所以,在 Kafka 裏面,信息會停留在隊列裏面,但對每一個程序來說,有一個單獨的記號,來記錄當前消費到了哪一條數據,如下圖所示。

當程序1要讀取 Kafka 裏面下一條數據時,Kafka 先把當前位置的標記向右移動一位,把新的這個值返回出來。標記移動與返回這兩個操作合在一起算是一個原子操作,不會出現重複讀取的問題。

程序1與程序2使用的是不同的標記,所以各自的標記指向哪個值,是互不影響的。

當增加一個程序3的時候,只需要再加一個標記即可。新的這個標記也不受前兩個標記的影響。

這就實現了在多個不同的程序讀取  Kafka 時,各自互不影響。

現在如果你覺得程序1消費太慢了,把程序1同時運行了3次,那麼由於標記和移位是原子操作,即使你看起來程序是同時去讀取 Kafka,但在內部 Kafka 也會對他們進行“排隊”,從而使得他們返回的結果不重複,不遺漏。

如果你在網上看 Kafka 的教程,你會發現他們提到了一個叫做 Offset 的東西,實際上就是本文所說的各個程序裏面指向當前數據的標記

你還會看到一個關鍵詞叫做Group,實際上對應到本文的程序1程序2程序3

對同一個隊列,如果多個程序使用不同的Group消費,那麼他們讀取的數據就互不干擾。

對同一個隊列,相同 Group 的多個進程在消費數據時,看起來就像是在對 Redis 進行 lpop 操作一樣。

最後,你在網上關於 Kafka 的文章裏面,一定會看到一個詞叫做Paritition或者中文分片。而且你會發現你無法理解這個東西。

沒關係,忘記它吧。你只需要知道,一個 Topic 有多少個 Partition,那麼你最多能啓動多少個進程讀取同一個 Group。——如果一個Topic有3個Partition,那麼你只能最多開3個進程同時讀相同的 Group。Topic如果有5個Partition,那麼你只能最多開5個進程讀同一個 Group。

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