Kafka 學習筆記(一) :為什麼需要 Kafka?

weixin_34148340發表於2018-03-14

本文由 GodPan 發表在 ScalaCool 團隊部落格。

我們在學習一個東西的時候,往往只有真正瞭解它背後的含義,才能一步一步的掌握它,直到運籌帷幄。對於Kafka來說,我也是一個小白,本篇文章我就以一個小白的角度來初探一下Kafka,本篇文章基於官方文件,順便說一句官方文件真的很重要,且讀且珍惜。

背景

Kafka最早是由LinkedIn公司開發的,作為其自身業務訊息處理的基礎,後LinkedIn公司將Kafka捐贈給Apache,現在已經成為Apache的一個頂級專案了,Kafka作為一個高吞吐的分散式的訊息系統,目前已經被很多公司應用在實際的業務中了,並且與許多資料處理框架相結合,比如Hadoop,Spark等。

訊息系統

在實際的業務需求中,我們需要處理各種各樣的訊息,比如Page View,日誌,請求等,那麼一個好的訊息系統應該擁有哪些功能呢?

  • 擁有訊息釋出和訂閱的功能,類似於訊息佇列或者企業訊息傳送系統;
  • 能儲存訊息流,並具備容錯性;
  • 能夠實時的處理訊息;

以上3點是作為一個好的訊息系統的最基本的能力。

那麼Kafka為什麼會誕生呢?

其實在我們工作中,相信有很多也接觸過訊息佇列,甚至自己也寫過簡單的訊息系統,它最基本應該擁有釋出/訂閱的功能,如下圖所示:

simple-message-system

其中消費者A與消費者B都訂閱了訊息源A和訊息源B,這種模式很簡單,但是相對來說也有弊端,比如以下兩點:

  • 該模式下消費者需要實時去處理訊息,因為這裡訊息源和消費者都不會維護一個訊息佇列(維護代價太大),這將會導致消費者若是暫時沒有能力消費,則訊息會丟失,當然也就不能獲得歷史的訊息;
  • 訊息源需要維護原本不屬於它的工作,比如維護訂閱者(消費者)的資訊,向多個消費者傳送訊息,亦或者有些還需要處理訊息反饋,這是原本純粹的訊息源就會變得越來越複雜;

當然這些問題都是可以改進的,比如我們可以在訊息源和消費者中間增加一個訊息佇列,如下圖所示:

simple-message-queue-system

從圖中我們可以看出,現在訊息源只需要將訊息傳送到訊息佇列中就行,至於其他就將給訊息佇列去完成,我們可以在訊息佇列持久化訊息,主動推訊息給已經訂閱了該訊息佇列的消費者,那麼這種模式還有什麼缺點嗎?

答案是有,上圖只是兩個訊息佇列,我們維護起來並不困難,但是如果有成百上千個呢?那不得gg,其實我們可以發現,訊息佇列的功能都很類似,無非就是持久化訊息,推送訊息,給出反饋等功能,結構也非常類似,主要是訊息內容,當然如果要通用化,訊息結構也要儘可能通用化,與具體平臺具體語言無關,比如用JSON格式等,所有我們可以演變出以下的訊息系統:

message-system

這個方式看起來只是把上面的佇列合併到了一起,其實並不那麼簡單,因為這個訊息佇列集合要具備以下幾個功能:

  • 能統一管理所有的訊息佇列,不是特殊需求不需要開發者自己去維護;
  • 高效率的儲存訊息;
  • 消費者能快速的找到想要消費的訊息;

當然這些只是最基本的功能,還有比如多節點容錯,資料備份等,一個好的訊息系統需要處理的東西非常多,很慶幸,Kafka幫我們做到了。

Kafka

在具體瞭解Kafka的細節前,我們先來看一下它的一些基本概念:

  • Kafka是執行在一個叢集上,所以它可以擁有一個或多個服務節點;
  • Kafka叢集將訊息儲存在特定的檔案中,對外表現為Topics;
  • 每條訊息記錄都包含一個key,訊息內容以及時間戳;

從上面幾點我們大致可以推測Kafka是一個分散式的訊息儲存系統,那麼它就僅僅這麼點功能嗎,我們繼續看下面。

Kafka為了擁有更強大的功能,提供了四大核心介面:

  • Producer API允許了應用可以向Kafka中的topics釋出訊息;
  • Consumer API允許了應用可以訂閱Kafka中的topics,並消費訊息;
  • Streams API允許應用可以作為訊息流的處理者,比如可以從topicA中消費訊息,處理的結果釋出到topicB中;
  • Connector API提供Kafka與現有的應用或系統適配功能,比如與資料庫聯結器可以捕獲表結構的變化;

它們與Kafka叢集的關係可以用下圖表示:

kafka-apis

在瞭解了Kafka的一些基本概念後,我們具體來看看它的一些組成部分。

Topics

顧名思義Topics是一些主題的集合,更通俗的說Topic就像一個訊息佇列,生產者可以向其寫入訊息,消費者可以從中讀取訊息,一個Topic支援多個生產者或消費者同時訂閱它,所以其擴充套件性很好。Topic又可以由一個或多個partition(分割槽)組成,比如下圖:

log-anatomy

其中每個partition中的訊息是有序的,但相互之間的順序就不能保證了,若Topic有多個partition,生產者的訊息可以指定或者由系統根據演算法分配到指定分割槽,若你需要所有訊息都是有序的,那麼你最好只用一個分割槽。另外partition支援訊息位移讀取,訊息位移有消費者自身管理,比如下圖:

log-consumer

由上圖可以看出,不同消費者對同一分割槽的訊息讀取互不干擾,消費者可以通過設定訊息位移(offset)來控制自己想要獲取的資料,比如可以從頭讀取,最新資料讀取,重讀讀取等功能。

關於Topic的分割槽策略以及與消費者間平衡後續文章會繼續深入講解。

Distribution

上文說到過,Kafka是一個分散式的訊息系統,所以當我們配置了多個Kafka Server節點後,它就擁有分散式的能力,比如容錯等,partition會被分佈在各個Server節點上,同時它們中間又有一個leader,它會處理所有的讀寫請求,其他followers會複製leader上的資料資訊,一旦當leader因為某些故障而無法提供服務後,就會有一個follower被推舉出來成為新的leader來處理這些請求。

Geo-Replication

異地備份是作為主流分散式系統的基礎功能,用於叢集中資料的備份和恢復,Kafka利用MirrorMaker來實現這個功能,使用者只需簡單的進行相應配置即可。

Producers

Producers作為訊息的生產者,可以自己指定將訊息釋出到訂閱Topic中的指定分割槽,策略可以自己指定,比如語義或者結構類似的訊息釋出在同一分割槽,當然也可以由系統迴圈釋出在每一個分割槽上。

Consumers

Consumers是一群消費者的集合,可以稱之為消費者組,是一種更高層次的的抽象,向Topic訂閱消費訊息的單位是Consumers,當然它其中也可以只有一個消費者(consumer)。下面是關於consumer的兩條原則:

  • 假如所有消費者都在同一個消費者組中,那麼它們將協同消費訂閱Topic的部分訊息(根據分割槽與消費者的數量分配),儲存負載平衡;
  • 假如所有消費者都在不同的消費者組中,並且訂閱了同個Topic,那麼它們將可以消費Topic的所有訊息;

下面是一個簡單的例子,幫助大家理解:

consumer-groups

上圖中有兩個Server節點,有一個Topic被分為四個分割槽(P0-P4)分別被分配在兩個節點上,另外還有兩個消費者組(GA,GB),其中GA有兩個消費者例項,GB有四個消費者例項。

從圖中我們可以看出,首先訂閱Topic的單位是消費者組,另外我們發現Topic中的訊息根據一定規則將訊息推送給具體消費者,主要原則如下:

  • 若消費者數小於partition數,且消費者數為一個,那麼它就消費所有訊息;
  • 若消費者數小於partition數,假設消費者數為N,partition數為M,那麼每個消費者能消費的分割槽數為M/N或M/N+1;
  • 若消費者數等於partition數,那麼每個消費者都會均等分配到一個分割槽的訊息;
  • 若消費者數大於partition數,則將會出現部分消費者得不到訊息分割槽,出現空閒的情況;

總的來說,Kafka會根據消費者組的情況均衡分配訊息,比如有訊息著例項當機,亦或者有新的消費者加入等情況。

Guarantees

kafka作為一個高水準的系統,提供了以下的保證:

  • 訊息的新增是有序的,生產者越早向訂閱的Topic傳送的訊息,會更早的被新增到Topic中,當然它們可能被分配到不同的分割槽;
  • 消費者在消費Topic分割槽中的訊息時是有序的;
  • 對於有N個複製節點的Topic,系統可以最多容忍N-1個節點發生故障,而不丟失任何提交給該Topic的訊息丟失;

相關這些點的細節,我準備再後續文章中再慢慢深入。

Kafka as a Messaging System

說了這麼多,前面也講了訊息系統的演變過程,那麼Kafka相比其他的訊息系統優勢具體在哪裡? 傳統的訊息系統模型主要有兩種:訊息佇列和釋出/訂閱。

1.訊息佇列

特性 描述
表現形式 一組消費者從訊息佇列中獲取訊息,訊息會被推送給組中的某一個消費者
優勢 水平擴充套件,可以將訊息資料分開處理
劣勢 訊息佇列不是多使用者的,當一條訊息記錄被一個程式讀取後,訊息便會丟失

2.釋出/訂閱

特性 描述
表現形式 訊息會廣播傳送給所有消費者
優勢 可以多程式共享訊息
劣勢 每個消費者都會獲得所有訊息,無法通過新增消費程式提高處理效率

從上面兩個表中可以看出兩種傳統的訊息系統模型的優缺點,所以Kafka在前人的肩膀上進行了優化,吸收他們的優點,主要體現在以下兩方面:

  • 通過Topic方式來達到訊息佇列的功能
  • 通過消費者組這種方式來達到釋出/訂閱的功能

Kafka通過結合這兩點(這兩點的具體描述檢視上面章節),完美的解決了它們兩者模式的缺點。

Kafka as a Storage System

儲存訊息也是訊息系統的一大功能,Kafka相對普通的訊息佇列儲存來說,它的表現實在好的太多,首先Kafka支援寫入確認,保證訊息寫入的正確性和連續性,同時Kafka還會對寫入磁碟的資料進行復製備份,來實現容錯,另外Kafka對磁碟的使用結構是一致的,就說說不管你的伺服器目前磁碟儲存的訊息資料有多少,它新增訊息資料的效率是相同的。

Kafka的儲存機制很好的支援消費者可以隨意控制自身所需要讀取的資料,在很多時候你也可以將Kafka作為一個高效能,低延遲的分散式檔案系統。

Kafka for Stream Processing

Kafka作為一個完美主義代表者,光有普通的讀寫,儲存等功能是不夠的,它還提供了實時處理訊息流的介面。

很多時候原始的資料並不是我們想要的,我們想要的是經過處理後的資料結果,比如通過一天的搜尋資料得出當天的搜尋熱點等,你可以利用Streams API來實現自己想要的功能,比如從輸入Topic中獲取資料,然後再發布到具體的輸出Topic中。

Kafka的流處理可以解決諸如處理無序資料、資料的複雜轉換等問題。

總結

訊息傳遞、儲存、流處理這麼功能單一來看確實很普通,但如何把它們完美的結合到一起,就是一種優雅的體現,Kafka做到了這一點。

相比HDFS分散式檔案儲存系統,雖然它能支援高效儲存並且批處理資料,但是它只支援處理過去的歷史資料。

相比普通的訊息系統來說,雖然能處理現在至未來的資料,但是它並不沒有儲存歷史的資料。

Kafka集眾家之所長,使整個系統能兼顧各方面的需求,可以用一個詞來說: “完美”!

本文從訊息系統的演變講起,到Kafka的具體組成,最後到Kafka的三大特性,旨在幫助大家能夠大概的瞭解Kafka是什麼的,到底有什麼作用,當然這只是一個小白的簡單理解,如有寫得不對的地方,希望大家能夠指出,不勝感激。

Kafka 學習筆記(一) :為什麼需要 Kafka?

相關文章