說說 MQ 之 Kafka

現代的互聯網分佈式系統,只要稍微大一些,就一定逃不開3類中間件:遠程調用(RPC)框架、消息隊列、數據庫訪問中間件。Kafka 是消息隊列中間件的代表產品,用 Scala 語言實現,本文采用的是 Kafka_2.11 0.10.0.0 版本進行實驗。

基本概念

首先,Kafka 中有一些基本的概念需要熟悉 1 2。

  • Topic,指消息的類別,每個消息都必須有;
  • Producer,指消息的產生者,或者,消息的寫端;
  • Consumer,指消息的消費者,或者,消息的讀端;
  • Producer Group,指產生者組,組內的生產者產生同一類消息;
  • Consumer Group,指消費者組,組內的消費者消費同一類消息;
  • Broker,指消息服務器,Producer 產生的消息都是寫到這裡,Consumer 讀消息也是從這裡讀;
  • Zookeeper,是 Kafka 的註冊中心,Broker 和 Consumer 之間的協調器,包含狀態信息、配置信息和一些 Topic 的信息;
  • Partition,指消息的水平分區,一個 Topic 可以有多個分區;
  • Replica,指消息的副本,為了提高可用性,將消息副本保存在其他 Broker 上。

特別說明,Broker 是指單個消息服務進程,一般情況下,Kafka 是集群運行的,Broker 只是集群中的一個服務進程,而非代指整個 Kafka 服務,可以簡單將 Broker 理解成服務器(Server)。Kafka 引入的術語都比較常見,從字面上理解相對直觀。Kafka 的大致結構圖是這樣:

說說 MQ 之 Kafka

Kafka 是 Pull 模式的消息隊列,即 Consumer 連到消息隊列服務上,主動請求新消息,如果要做到實時性,需要採用長輪詢,Kafka 在0.8的時候已經支持長輪詢模式。上圖中 Consumer 的連接箭頭方向可能會讓讀者誤以為是 Push 模式,特此註明。更多關於 Kafka 設計的文章可以參考官方文檔,或者一些比較好的博客文章 3。

關於順序和分區

Kafka 是一個力求保持消息順序性的消息隊列,但不是完全保證,其保證的是 Partition 級別的順序性,如下圖:

說說 MQ 之 Kafka

此圖是 Topic 的分區 log 的示意圖,可見,每個分區上的 log 都是一個有序的隊列,所以,Kafka 是分區級別有序的。如果,某個 Topic 只有一個分區,那麼這個 Topic 下的消息就都是有序的。

分區是為了提升消息處理的吞吐率而產生的,將一個 Topic 中的消息分成幾份,分別給不同的 Broker 處理。如下圖:

說說 MQ 之 Kafka

此圖中有2個 Broker,Server 1 和 Server 2,每個 Broker 上有2個分區,總共4個分區,P0 ~ P3;有2個 Consumer Group,Consumer Group A 有2個 Consumer,Consumer Group B 有4個 Consumer。Kafka 的實現是,在穩定的情況下,維持固定的連接,每個 Consumer 穩定的消費其中某幾個分區的消息,以上圖舉例,Consumer Group A 中的 C1 穩定消費 P0、P3,C2 穩定消費 P1、P2。這樣的連接分配可能會導致消息消費的不均勻分佈,但好處是比較容易保證順序性。

維持完全的順序性在分佈式系統看來幾乎是無意義的。因為,如果需要維持順序性,那麼就只能有一條線程阻塞的處理順序消息,即,Producer -> MQ -> Consumer 必須線程上一一對應。這與分佈式系統的初衷是相違背的。但是局部的有序性,是可以維持的。比如,有30000條消息,每3條之間有關聯,1->2->3,4->5->6,……,但是全局範圍來看,並不需要保證 1->4->7,可以 7->4->1 的順序來執行,這樣可以達到最大並行度10000,而這通常是現實中我們面對的情況。通常應用中,將有先後關係的消息發送到相同的分區上,即可解決大部分問題。

關於副本

副本是高可用 Kafka 集群的實現方式。假設集群中有3個 Broker,那麼可以指定3個副本,這3個副本是對等的,對於某個 Topic 的分區來說,其中一個是 Leader,即主節點,另外2個副本是 Follower,即從節點,每個副本在一個 Broker 上。當 Leader 收到消息的時候,會將消息寫一份到副本中,通常情況,只有 Leader 處於工作狀態。在 Leader 發生故障宕機的時候,Follwer 會取代 Leader 繼續傳送消息,而不會發生消息丟失。Kafka 的副本是以分區為單位的,也就是說,即使是同一個 Topic,其不同分區的 Leader 節點也不同。甚至,Kafka 傾向於用不同的 Broker 來做分區的 Leader,因為這樣能做到更好的負載均衡。

在副本間的消息同步,實際上是複製消息的 log,複製可以是同步複製,也可以是異步複製。同步複製是說,當 Leader 收到消息後,將消息寫入從副本,只有在收到從副本寫入成功的確認後才返回成功給 Producer;異步複製是說,Leader 將消息寫入從副本,但是不等待從副本的成功確認,直接返回成功給 Producer。同步複製效率較低,但是消息不會丟;異步複製效率高,但是在 Broker 宕機的時候,可能會出現消息丟失。

關於丟消息和重複收到消息

任何一個 MQ 都需要處理丟消息和重複收到消息的,正常情況下,Kafka 可以保證:1. 不丟消息;2. 不重複發消息;3. 消息讀且只讀一次。當然這都是正常情況,極端情況,如 Broker 宕機,斷電,這類情況下,Kafka 只能保證 1 或者 2,無法保證 3。

在有副本的情況下,Kafka 是可以保證消息不丟的,其前提是設置了同步複製,這也是 Kafka 的默認設置,但是可能出現重複發送消息,這個交給上層應用解決;在生產者中使用異步提交,可以保證不重複發送消息,但是有丟消息的可能,如果應用可以容忍,也可以接受。如果需要實現讀且只讀一次,就比較麻煩,需要更底層的 API 4。

最後送福利了,現在私信我“資料”即可獲取Java工程化、高性能及分佈式、高性能、高架構、性能調優、Spring、MyBatis、Netty源碼分析等多個知識點高級進階乾貨的直播免費學習權限及相關視頻資料,還有spring和虛擬機等書籍掃描版


分享到:


相關文章: