《Kafka技術內幕》讀書筆記:Kafka入門

1. 介紹

1.1 Kafka流式處理平臺

  一個流式數據平臺,最重要的是要具備如下3個特點:

  • 類似消息系統,提供事件流的發佈和訂閱,既具備事件注入功能。
  • 存儲事件流的數據節點具有故障容錯的特點,即具備數據存儲功能。
  • 能夠對實時的事件流進行流式地處理和分析,即具備流處理功能。

作爲一個流式數據平臺,Kafka如何實現上面3個功能特點?

1.1.1 消息系統

  消息系統(也叫消息隊列)主要有兩種消息模型隊列發佈訂閱

  • 隊列模式:多個消費者讀取隊列,每條消息只發送給一個消費者
  • 發佈-訂閱模式:多個消費者訂閱主題,主題的每條記錄會發布給所有的消費者。

  Kafka使用消費組統一了兩種消息模型

1.1.2 存儲系統

  任何消息隊列要做到發佈消息和消費消息的解耦合,實際上都要扮演一個存儲系統的角色,負責保存還沒有被消費的消息。如果消息只是在內存中,一單機器宕機或重啓,內存中的消息就會全部丟失。Kafka也不例外,數據寫入到Kafka集羣的服務器節點時,黑灰賦值多份來保障出現故障時仍能可用。爲了保證消息的可靠存儲,Kafka還允許生產者的生產請求在收到應答結果前,阻塞式地等待一條消息,直到它完全地複製到多個節點上,才認爲這條消息寫入成功。

1.1.3 流處理系統

  流式數據平臺僅有消息的讀取和寫入、消息的存儲是不夠的,還需要流式數據處理能力。對於簡單的處理,可直接使用Kafka提供的生產者API和消費者API來完成;但對於複雜的業務邏輯處理,Kafka提供了完整的流處理API,比如流的聚合、連接、各種轉換操作。Kafka流處理框架內部解決很多流處理應用都會面臨的問題:處理亂序或遲來的數據、重新處理輸入數據、窗口和狀態操作等。

1.2 Kafka將消息系統、存儲系統。流處理系統組合在一起

  • 傳統消息系統的流處理通常只會處理訂閱動作發生後纔到達的消息,無法處理訂閱之前的歷史數據
  • 分佈式文件存儲系統一般存儲靜態的歷史數據,對歷史數據的處理一般採用批處理方式

  Kafka將消息系統、存儲系統、流處理系統都組合在一起,構成了以Kafka爲中心的的流式處理數據處理平臺。它既能處理最新的實時數據,也能處理過去的歷史數據,其主要包括4種核心API:

  • 生產者API:應用程序發佈事件流到一個或多個主題
  • 消費者API:應用程序訂閱一個或多個主題,並處理事件流
  • 連接器API:將Kafka主題和已有的數據源進行連接,數據可以相互導入導出
  • 流處理API:從Kafka主題消費輸入流,經過處理後,產生出輸出流到輸出主題
    在這裏插入圖片描述
      建立以Kafka爲核心的流式數據管道,不僅要保證低延遲的消息處理,還需要保證存儲的可靠性。在和離線系統集成時,將Kafka數據加載到批處理系統時,要保證數據不遺漏。

2. Kafka基本概念

  先拋出3個問題,在回答這些問題時需要引入很多概念:

  1. Kafka的主題與分區內部是如何存儲的,它們有什麼特點?
  2. 與傳統的消息系統相比,Kafka的消費模型有什麼特點?
  3. Kafka如何實現分佈式的數據存儲與數據讀取?

2.1 分區模型

  Kafka集羣爲每個主題維護了分佈式的分區( partition )日誌文件,物理意義上可以把主題看作分區的日誌文件( partitioned log)。 每個分區都是一個有序的、不可變的記錄序列,新的消息會不斷追加到提交日誌( commit log)。 分區中的每條消息都會按照時間順序分配到一個單調遞增的順序編號,作偏移量( offset ),這個偏移量能夠唯一地定位當前分區中的每一條消息。每個分區的偏移量都從0開始,不同分區間的偏移量都是獨立的,不會互相影響。
在這裏插入圖片描述
  如上圖所示,主題有3個分區,每條消息包括鍵值和時間戳,消息到達後會按照規則到指定分區,得到一個分區內的自增偏移量,原始的消息內容和分配到的偏移量以及其他一些元數據信息會存儲到分區日誌文件中。

  • 傳統消息系統在服務端保持消息的順序,如果多個消費者消費同一個消息隊列,服務端會以消息存儲的的順序依次發送給消費者。但由於消息是異步發送的,消息到達消費者的順序可能是無序的,這樣消息無法很好的保證消息會被順序處理。
  • Kafka比傳統的消息系統有更強的順序性保證,以主題分區作爲消息處理的並行單元。一個topic下的一個分區,在同一個消費組下,僅對應一個消費者(反之,同消費組下的多個消費者可以對應同一個分區),即這個消費者在消費組中就是這個分區的唯一讀取線程,這樣增強了順序行,同時也做到了消費組內消費者的負載均衡。

2.2 消費模型

  基於推送模型的消息系統,由消息代理記錄消費者的消費狀態。消息代理在消息推送到消費者後,標記這條消息爲已消費。但是,如果消息代理將消息發出後,消費進程掛掉或網絡原因消費者沒有收到消息時,就可能造成消息丟失。要保證消息的處理語義,消息代理髮送完消息後,要設置狀態爲已發送,只有收到消費者的確認請求才更新爲已消費,這需要在消息代理中記錄所有消息的消費狀態。
     Kafka採用拉取模型,由消費者自己記錄消費狀態,每個消費者獨立地順序讀取每個分區的消息。

在這裏插入圖片描述
  如圖所示,有不同消費組的兩個消費者訂閱了同一個主題,並且分到了同一個分區,消費者A的進度爲3,消費者B的進度是6。消費者拉取的最大上限通過最高水位(watermark)控制,生產者最新寫入的消息如果還沒有到達備份數量,對消費者是不可見的。這種由消費者控制偏移量的優點是:消費者讀取間不受影響,可以按照任意順序消費消息,甚至消費者可以充值偏移量,重新讀取之前已經消費過的消息
  在一些消息系統中,消息代理會在消息被消費後立即刪除消息。如果有不同類型的消費者訂閱同一個主題,消息代理可能需要冗餘地存儲同一條信息;或者等素有消費者都消費完才刪除,這就需要消息消息代理跟蹤每個消費者的消費狀態,這種設計很大程度上限制了消息系統的整體吞吐量和處理延遲。Kafka的做法是生產者發佈的消息會一直保存在Kafka集羣中,不管消息有沒有被消費。用戶可以通過設置保留時間來清理過期數據。

2.3 分佈式模型

  Kafka每個主題的多個分區日誌分佈式的存儲在Kafka集羣上,同時爲了故障容錯,每個分區都會以副本的方式複製到多個消息代理節點上,其中一個節點作爲主副本(Leader),其他節點作爲備份副本(Follower)。主副本會負責客戶端的所有讀寫操作,備份副本僅僅從主副本同步數據。當主副本出現故障時,本分副本中的一個副本會被選擇爲新的主副本。即每個分區的副本中只有主副本負責接受讀寫,所以每個服務端都會作爲某些主分區的副本,以及另外一些分區的本分副本。這樣Kafka集羣的所有服務端整體上對客戶端是負責均衡的。
  Kafka的生產者和消費者和消費者對於服務端來說都是客戶端,生產者客戶端發佈消息到服務端的指定主題,會指定消息所屬的分區。根據消息是否有鍵採用不同的分區策略:有鍵則Hash,無鍵則輪詢
在這裏插入圖片描述

  • Kafka的消費者通過訂閱主題來消費消息,並且每個消費者都會設置一個消費組名稱。因爲生產者發佈到主題的每一條消息都只會發送給消費組的的一個消費者(消息提交的分區對應的消費者)。所以如果要實現傳統消息系統的"隊列"模型,可以讓每個消費者擁有相同的消費組名稱,這樣在這個消費組下,這些主題的消消息就會負載均衡到所有消費者;若果要實現發佈-訂閱模式,則可以消費者在不同的消費組,這樣這寫消息會廣播給所有的消費者。
  • 同一個消費組下多個消費者互相協調消費工作,消費組成員列表由Kafka的消費組管理協議動態的維護,當一個消費者重新加入消費組,或者有消費者離開消費組時,主題下的分區會重新分配給消費組中的各個消費者。
  • Kafka的消費者在消費消息時,只保證在一個分區內消息的完全有序性,並不保證同一個主題中多個分區間的消息順序。若果業務上要保證所以消息完全順序一致,只能通過該主題設置一個分區來完成,這樣吞吐量會降低。一般來說,只需要保證每個分區的有序性,在對消息加上鍵來保證相同鍵的所有消息落入同一個分區,就可以滿足絕大多數應用。

3. Kafka的設計與實現

同樣地先拋出3個問題:

  • 如何利用操作系統的優化技術來高效地持久化日誌文件和加快數據傳輸效率?
  • Kafka的生產者如何批量地發送消息,消費者採用拉取模型帶來哪些優點?
  • Kafka的副本機制如何工作,故障發生時怎麼保證數據不丟失?

3.1 文件系統的持久化與數據傳輸效率

  人們普遍認爲一旦涉及磁盤訪問,讀寫的性能就嚴重下降。實際上,現代操作系統針對磁盤的讀寫已經做了一些優化方案來加快磁盤的訪問速度。

  • 預讀(read-ahead)提前將大的磁盤塊讀入內存。
  • 後寫(write-behind)會將很多小的邏輯寫操作合併起來組合成一個大的物理寫操作。
  • 操作系統還會將主內存剩餘的所有空閒的空間都用作磁盤緩存(dist cache/page cache),所有的磁盤讀寫操作都會經過統一的磁盤緩存(除了直接IO迴繞過磁盤緩存)。
    在這裏插入圖片描述
    綜合這幾點優化特點,如果是針對磁盤的順序訪問,某些情況下它可能比隨機的內存訪問都要快,甚至可以和網絡的速度相差無幾。

  消息系統內的消息從生產者保存到服務端,再從服務端讀取出來,數據的傳出效率決定了生產者和消費者的性能。生產者如果每發送一條消息都直接通過網絡發送到服務端,勢必會造成過多的網絡請求。如果我們能夠將多條消息按照分區進行分組,並採用批量的方式一次發送一個消息集,並且對消息集進行壓縮,就可以減少網絡傳輸的帶寬,進一步提高數據的傳輸效率。
  消費者要讀取服務端的數據,需要將服務端的磁盤文件通過網絡發送到消費者進程,而網絡發送通常涉及不同的網絡節點。傳統的讀取磁盤文件在每次發送網絡時,都需要將頁面緩存先保存到用戶緩存,然後讀取消息時再將其複製到內核空間,步驟如下:

  1. 操作系統將數據從磁盤中讀取文件到內核空間裏面的頁面緩存。
  2. 應用程序將數據從內核空間讀入用戶空間緩衝區。
  3. 應用程序將讀到的數據寫會內核空間並放入socket緩衝區。
  4. 操作系統將數據從socket緩衝區複製到網卡接口,此時數據才能通過網絡發送出去。

  結合Kafka的消息有多個訂閱者的使用場景,生產者發佈的消息一般會被不同的消費者消費多次,數據傳輸十分頻繁,使用"零拷貝技術"只需將磁盤文件的數據複製到頁面緩存一次,然後將數據聰明和頁面緩衝直接發送到網絡中(發送給不同的使用者可以重複使用同一個頁面緩存),避免了重複的複製操作。這樣,消息的使用速度基本上等同於網絡連接的速度了。
在這裏插入圖片描述
  對比優化前後的兩種方案。假設有10個消費者,傳統複製方式的數據複製次數爲4 x 10 = 40次,而"零拷貝技術"只需要將磁盤文件讀入頁面緩存1次加上10個消費者各讀取頁面緩存1次到網卡接口,共11次拷貝。顯然減少了數據的複製次數,提高了消費性能。

3.2 消息的生產與消費

3.2.1 生產

  Kafka的生產者將消息直接發送給分區主副本的消息代理節點,並不需要經過中間路由層,爲了做到這一點,所有消息代理節點在發送消息之前,會向任意一個代理節點請求元數據,並確定每條消息對應的目標節點(分區對應的主節點),然後發送出去。分區的選擇規則如下:

  1. 生產者指定了分區;
  2. 鍵值存在,則可以使用"分區語義函數"將相同鍵的所有消息發佈到同一分區。用戶可通過Kafka暴露的分區語義接口指定鍵參與分區的規則。
  3. 採用輪詢方式選定分區。

  前面說過Kafka會將生產者的消息按照分區分組,同一分區的消息批量壓縮發送,減少了網絡請求。對於緩衝的調節我們可以在生產者客戶端設置消息大小上限延遲時間,達到消息大小上限或延遲時間,都會觸發網絡請求。

3.2.2 消費

  Kafka消息消費採用拉取模型,和生產者採用批量發送消息類似,消費者拉取消息可以一次拉取一批消息。拉取模型雖然不用消息代理記錄消息的消費狀態,但也會有一個缺點:消息代理沒有數據或者數據量很少,消費者可能需要不斷的輪詢,並等待新數據。可以通過允許消費者拉取請求以阻塞式、長輪詢的方式等待,直到有新的數據到來。我們可以在消費者客戶端設置指定字節數量,表示在消息代理在還沒有收集到足夠的數據時,客戶端的拉取請求不會立即返回。

3.3 副本機制和容錯處理

  Kafka的每個Broker在分區的層面上互爲備份。本分副本始終儘量保持與主副本的數據同步。備份副本的日誌文件和主副本的日誌總是相同的,它們都有相同的偏移量和相同順序的消息。備份副本從主副本消費消息的方式和普通消費者一樣,只不過備份副本會將處理寫入到本地日誌文件。
  分佈式系統處理故障容錯時,需要明確定義節點是否處於存活狀態。Kafkaf對接點的存貨定義有兩個條件:

  • 節點必須和ZooKeeper保持會話;
  • 如果節點是某個分區的備份副本,它必須與主副本的寫寫操作進行復制,並且複製的進度不能太落後

  滿足這兩個條件,叫作"正在同步中"(in-sync)。每個分區的主副本會跟蹤正在同步中的備份副本節點(In Sync Replicas,ISR)。如果一個備份副本掛掉、沒有響應或者落後太多,主副本會將其從同步副本集合中移除。反之副本重新趕上主副本,它就會被重新加入集合中。
  在Kafka中,一條消息只有被ISR集合中的所有副本都運用到本地的日誌文件,纔會認爲消息被成功提交了。任何時刻,只要ISR至少有一個副本是存活的,Kafka就可以保證消息被提交就不會丟失。

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