1. 程式人生 > >Kafka工作流程-Kafka 消費者

Kafka工作流程-Kafka 消費者

1. 使用消費者組實現訊息佇列的兩種模式

    Kafka 叢集的資料需要被不同型別的消費者使用,而不同型別的消費者處理邏輯不 同。Kafka 使用消費組的概念,允許一組消費者程序對消費工作進行劃分。每個消費者都可 以配置一個所屬的消費組,並且訂閱多個主題。Kafka 會發送每條訊息給每個消費組中的一 個消費者程序( 同一條訊息廣播給多個消費組,單播給同一組中的消費者)。被訂閱主題 的所有分割槽會平均地負載給訂閱方,即消費組中的所有消費者。

    Kafka 採用消費組保證了“一個分割槽只可被消費組中的一個消費者所消費” ,這意味 著:

(1) 在一個消費組中,一個消費者可以消費多個分割槽。

(2) 不同的消費者消費的分割槽一定不會重複,所有消費者一起消費所有的分割槽。

(3) 在不同消費組中,每個消費組都會消費所有的分割槽。

(4) 同一個消費組下消費者對分割槽是互斥的,而不同消費組之間是共享的。

    下圖給出了多個消費者都在同一個消費組中,或者各向組成一個消費組的不同消費場 景,這樣 Kafka 也可以實現傳統訊息佇列的釋出——訂閱模式和佇列模式。

    釋出-訂閱模式:同一條訊息會被多個消費組消費,每個消費組只有一個消費者,實現 廣播。

    佇列模式:只有一個消費組、多個消費者一條訊息只被消費組的一個消費者消費,實現 單播。

2. 消費者組再平衡實現故障容錯

    消費者是客戶端的監務處理邏輯程式,因此要考慮消費者的故障容錯。一個消費組有多 個消費者,因此消費組需要維護所有的消費者。如果一個消費者宕機了,分配給這個消費者 的分割槽需要重新分配給相同組的其他消費者;如果一個消費者加入了同一個組,之前分配給 其他消費組的分割槽需要分配給新加入的消費者。

    一旦有消費者加入或退出消費組,導致消費組成員列表發生變化,消費組中所有的消費者就要執行再平衡( rebalance ) 工作。如果訂閱主題的分割槽有變化,所有的消費者也都 要再平衡。如下圖所示,在加入一個新的消費者後,需要為所有的消費者重新分配分割槽, 因此所有消費者都會執行再平衡。

    消費者再平衡前後分配到的分割槽會完全不同,那麼消費者之間如何確保各向消費訊息的 平滑過渡呢?假設分割槽 P1 原先分配給消費者 C1 ,再平衡後被分配給消費者 C2 。如果再 平衡前消費者 C1 儲存了分割槽 P1 的消費進度,再平衡後消費者 C2 就可以從儲存的進度位 置繼續讀取分割槽 P1,保證分割槽 P1 不管分配給哪個消費者,訊息都不會丟失,實現了消費者 的故障容錯。

(1) 儲存消費進度 

    生產者的提交日誌採用遞增的偏移量,連同訊息內容一起寫入本地日誌檔案。生產者客戶端不需要儲存偏移量相關的狀態,消費者客戶端則要儲存消費訊息的偏移量即消費進度。 消費進度表示消費者對一個分割槽已經消費到了哪裡。

    由於消費者消費訊息的最小單元是分割槽,因此每個分割槽都應該記錄消費進度,而且消費進度應該面向消費組級別。假設面向的是消費者級別,再平衡前分割槽 P1 只記錄到消費者 C1 中,再平衡後分區 P1 屬於消費者 C2。但是這樣一來,分割槽 P1 和消費者 C2 之前沒有記錄任何資訊,就無法做到無縫遷移。而如果針對消費組,因為消費者 C1 和消費者 C2 屬於同 一個消費組,再平衡前記錄分割槽 P1 到消費組 l ,再平衡後消費者 C2 可以正常地讀取消費 組 l 的分割槽 P1 進度,還是可以準確還原出這個分割槽在消費組 l 中的最新進度的。總結下, 雖然分割槽是以消費者級別被消費的,但分割槽的消費進度要儲存成消費組級別的。

    消費者對分割槽的消費進度通常儲存在外部儲存系統中,比如 ZK 或者 Kafka 的內部主 題(__consumer_offsets)。這樣分割槽的不同擁有者總是可以讀取同一個儲存系統的消費進度, 即使消費者成員發生變化,也不會影響訊息的消費和處理。如下體所示,消費者消費訊息時, 需要定時將分割槽的最新消費進度儲存到 ZK 中。當發生再平衡時,消費者擁有的新分割槽消 費進度都可以從 ZK 中讀取出來,從而恢復到最近的消費狀態。

     由消費者儲存消費進度的另一個原因是: 消費者消費訊息是主動從服務端拉取資料, 而不是由服務端向消費者推送資料。如果由服務端推送資料給消費者,消費者只負責接收數 據,就不需要儲存狀態了。但後面這種方法會嚴重影響服務端的效能,因為要在服務端記錄 每條訊息分配給哪個消費者,還要記錄消費者消費到哪裡了。

(2) 消費者與 ZK 的關係

    消費者除了需要儲存消費進度到 ZK 中,它分配的分割槽也是從 ZK 讀取的。ZK 不僅儲存了 Kafka 的內部元資料,而且記錄了消費組的成員列表、分割槽的消費進度、分割槽的所有者。

    消費者要消費哪些分割槽的訊息由消費組來決定,因為消費組管理所有的消費者,所以它 需要知道叢集中所有可用的分割槽和所有存活的消費者,才能執行分割槽分配演算法,而這些資訊 都需要儲存到 ZK 中。每個消費者都要在 ZK 的消費組節點下注冊對應的消費者節點,在分 配到不同的分割槽後,才會開始各自拉取分割槽的訊息。

    通常,客戶端程式碼並不直接完成上面那些複雜的操作步驟,而是由服務端暴露出一個 API 介面,讓客戶端可以透明地和叢集互動。這個 API 介面實際上屬於客戶端程序範疇,用 來和    管理員以及資料儲存節點通訊。Kafka 提供了兩種層次的客戶端 API : 如果消費者不 太關心訊息偏移量的處理,可以使用高階API ;如果想自定義消費邏輯,可以使用低階API 。

    高階 API:消費者客戶端程式碼不需要管理偏移量的提交,並且採用了消費組的自動負載均衡功能,確保消費者的增減不會影響訊息的消費。高階 API 提供了從 Kafka 消費資料的高層抽象。

    低階 API:通常針對特殊的消費邏輯,比如消費者只想消費某些特定的分割槽。低階 API 的客戶端程式碼需要自己實現一些和 Kafka 服務端相關的底層邏輯,比如選擇分割槽的主副本、 處理主副本的故障轉移等。