Kafka的原理介紹及實踐

網易雲信發表於2020-07-09

一.官方定義

根據官網的介紹,kafka是一個提供統一的、高吞吐、低延遲的,用來處理實時資料的流式平臺,它具備以下三特性:

  1. 流式記錄的釋出和訂閱:類似於訊息系統。
  2. 儲存:在一個分散式、容錯的叢集中安全持久化地儲存流式資料。
  3. 處理:編寫流處理應用程式,對實時事件進行響應。

kafka一般用在兩大類應用中:

  1. 建立實時流資料管道,在系統或應用之間實時地傳輸資料。
  2. 構建對資料流進行轉換和處理的實時流應用程式。

在郵箱服務中,我們主要將kafka作為訊息系統,用於系統內部訊息的傳輸。 為什麼要採用kafka呢?讓我們先從kafka的設計原理說起。

概念與儲存機制

kafka中是以Topic機制來對訊息進行分類的,同一類訊息屬於同一個Topic,你可以將每個Topic看成是一個訊息佇列。生產者將訊息傳送到相應的Topic,而消費者通過從Topic拉取訊息來消費,沒錯,在kafka中是要求消費者主動拉取訊息消費的,它並不會主動推送訊息,這是它的一個特點,為什麼會這樣設計呢?我們後面再說,先來看一下Topic的結構:

Partition分割槽,每個topic可以有多個分割槽,這是kafka為了提高併發量而設計的一種機制:一個topic下的多個分割槽可以併發接收訊息,同樣的也能供消費者併發拉取訊息,即分割槽之間互不干擾,這樣的話,有多少個分割槽就可以有多大的併發量。所以,如果要更準確的打比方,一個分割槽就是一個訊息佇列,只不過這些訊息佇列同屬於一種訊息分類。

在kafka伺服器,分割槽是以目錄形式存在的,每個分割槽目錄中,kafka會按配置大小或配置週期將分割槽拆分成多個段檔案(LogSegment), 每個段由三部分組成:

  1. 磁碟檔案:*.log
  2. 位移索引檔案:*.index
  3. 時間索引檔案:*.timeindex

其中.log用於儲存訊息本身的資料內容,.index儲存訊息在檔案中的位置(包括訊息的邏輯offset和物理儲存offset),*.timeindex儲存訊息建立時間和對應邏輯地址的對映關係。

段檔案結構圖如下 : enter image description here

將分割槽拆分成多個段是為了控制儲存的檔案大小,如果整個分割槽只儲存為一個檔案,那隨著分割槽裡訊息的增多,檔案也將越來越大,最後不可控制。而如果每個訊息都儲存為一個檔案,那檔案數量又將變得巨大,同樣容易失去控制。所以kafka採用段這種方式,控制了每個檔案的大小,也方便控制所有檔案的數量。同時,這些檔案因為大小適中,可以很方便地通過作業系統mmap機制對映到記憶體中,提高寫入和讀取效率。這個設計的另一個好處是:當系統要清除過期資料時,可以直接將過期的段檔案刪除,非常簡潔。

但是這裡也會有一個問題:如果每個訊息都要在index檔案中儲存位置資訊,那麼index檔案也很容易變得很大,這樣又會減弱上文所說的好處。所以在kafka中,index設計為稀疏索引來降低index的檔案大小,這樣,index檔案儲存的實際內容為:該段訊息在訊息佇列中的相對offset和在log檔案中的物理偏移量對映的稀疏記錄。

那麼多少條訊息會在index中儲存一條記錄呢?這個可以通過系統配置來進行設定。索引記錄固定為8個位元組大小,分別為4個位元組的相對offset(訊息在partition中全域性offset減去該segment的起始offset),4個位元組的訊息具體儲存檔案的物理偏移量。

index檔案結構圖如下: enter image description here

Kafka不會在消費者拉取完訊息後馬上就清理訊息,而是會儲存段檔案一段時間,直到其過期再標記為可清理,由後臺程式定期進行清理。這種機制使得消費者可以重複消費訊息,滿足更靈活的需求。

查詢機制

上面說過,kafka雖然作為訊息系統,但是消費訊息並不是通過推送而是通過拉取來消費的,client需要通過offset和size引數主動去查詢訊息。

kafka收到客戶端請求後,對訊息的定址會經過下面幾個步驟:

  1. 查詢具體的Log Segment,kafka將段資訊快取在跳躍表中,所以這個步驟將從跳躍表中獲取段資訊。
  2. 根據offset在index檔案中進行定位,找到匹配範圍的偏移量position,此時得到的是一個近似起始檔案偏移量。
  3. 從Log檔案的position位置處開始往後尋找,直到找到offset處的訊息。

enter image description here

二、RabbitMQ vs kafka

介紹了kafka的實現原理,我們再來對比一下同樣作為訊息佇列服務的RabbitMQ。MQ的應用也很廣泛,功能多而全,那麼和MQ相比,kafka有哪些優勢呢?為什麼我們會使用kafka而拋棄了RabbitMQ呢?

RabbitMQ流程圖: enter image description here

RabbitMQ消費者只能從佇列頭部按序進行消費,訊息一旦被消費,就會被打上刪除標記,緊接著消費下一條訊息,沒辦法進行回溯操作,這樣的話一個消費者消費完訊息,另一個消費者就別想再消費了。而Kafka提供動態指定消費位點,能夠靈活地進行回溯消費操作,只要該訊息還在生命週期內可以重複拉取,並且不同消費者可以互不干擾的消費同一個訊息佇列,這就比RabbitMQ靈活多了。

kafka消費位點示意圖: enter image description here

RabbitMQ如果要滿足多個消費者消費同一個訊息佇列,也可以藉助exchange路由能力,但是這樣會將訊息複製到多個佇列,每個消費者需要繫結一個自己的佇列進行消費。如果有幾百個消費者,那麼佇列複製幾百倍,引起mq的訊息水位猛漲,容易失控。而kafka就沒這個問題,不管多少個消費者都只需要一個佇列就能滿足,每個消費者都可以完整地不相互干擾地消費佇列中的所有訊息。

當然,RabbitMQ也有其優點,它提供的exchange,binding, queue等抽象實體,提供強大的路由關係(rounte key and bindkey)和訊息過濾能力。作為傳統訊息系統提供了細粒度的訊息控制能力。而Kafka主要是面向高流量,大吞吐的批處理系統,在路由抽象方面化繁為簡,重點關注系統的高吞吐,所以使用上更為簡潔。 kafka還有傳統解決方案無法滿足的高伸縮能力等優勢,這裡就不一一介紹了。

三、Kafka在郵件系統data bus中的運用

正因為kafka有著以上介紹的能力和優勢,我們的郵箱服務中採用了它作為訊息系統,其中一個應用就是郵件系統的data bus。

data bus介紹

郵件系統使用者收發信流程伴隨著大量的業務邏輯和子系統呼叫,如果將這些流程都強依附在主幹枝上,將會對系統造成較大的壓力,整個業務流程也將變得複雜而緩慢。所以通過資料匯流排將主次流程進行解耦,減輕收發信主流程的複雜度,使其可以以更快的速度完成,加快系統響應時間。主流程產生事件源,通過kafka的傳輸,觸發多個次要流程,次要流程可以併發在系統後臺完成,並且可以輕易的擴充套件多種多樣的次要流程。

下圖以簡化後的信流程為例: enter image description here

Kafka在data bus中的運用

郵件系統在完成收發信流程後,會生成當次流程相關的系統事件,比如新郵件事件。data bus將這些事件寫入到kafka叢集的相應topic中,下游的一系列子系統對topic進行消費。

  1. 每個不同的流程會對應不用的topic,以區分不同類別的事件,比如新進郵件,郵件已讀,郵件刪除等。

  2. 每個topic可以根據各自的訊息吞吐量和併發需求劃分成多個partition,比如新進郵件量大可以劃分成256個分割槽,郵件刪除量小則可以劃分32個分割槽。

  3. 每個事件按什麼機制來分配到相應的分割槽呢?一般來說可以按郵筒來劃分,同一個郵筒的事件進入同一個partition,這樣就保證了同一郵筒發生的事件的順序。

  4. 不同事件的時效性可能有不同,所以其需要儲存的時間也可以不同,可以根據業務的需求來設定topic的保留時長。

  5. 由於事件全部寫入到kafka中,後臺任務可以任意消費,所以可以靈活地增加不同的業務流程。

如下圖所示,應用消費能力能借助Kafka叢集實現彈性擴容 enter image description here

總 結

kafka在郵件系統中的應用給我們帶來的好處:

• 時延敏感型業務:通過提高業務Topic的Partition數量,一來留下了較好的機器擴容的空間,另一方面也可以通過提高消費者併發執行緒數來提升應用整體消費速度,減少時延。

• 慢速型業務:有些不關心時效性的下游業務,在考慮訊息生命週期等因素,可以很好地利用Kafka的訊息堆積能力,磁碟儲存能力,削峰填谷,讓消費流速適應自己的處理能力,不至於因為突然間的大量訊息衝擊而崩潰。

相關文章