如何在Kafka中將嚴格順序與大規模並行性結合? - Emil

發表於2021-01-27

參與了多個針對各個行業的不同客戶的大型Kafka專案之後,遭遇一個似乎永遠不會過時的問題是:如何保持嚴格的順序,同時仍然並行處理記錄?

這是一個公平的問題。嚴格的順序是等於序列化,其概念似乎與並行性的目標相矛盾。

 

部分順序和總順序

我們將首先探討順序的概念。

如事件流平臺所期望的那樣,Kafka保留已釋出記錄的順序,前提是這些記錄佔用相同的分割槽。為了理解這在實踐中的含義,需要探索Kafka主題的體系結構以及底層的分片機制-分割槽。

一個分割槽是一個完全有序序列的記錄,而且是卡夫卡的根本。一條記錄具有一個ID —一個64位的整數偏移量和一個毫秒級的時間戳。另外,它可能有一個鍵和一個值;兩者都是位元組陣列,並且都是可選的。術語“完全排序”僅表示對於任何給定的生產者,記錄將按照應用程式發出的順序進行寫入。

如果記錄P在Q之前釋出,則P將在分割槽中的Q之前。(假設P和Q共享一個分割槽。)此外,所有使用者將以相同的順序讀取它們;對於每個可能的使用者,將始終在Q之前讀取P。在大多數用例中,這種順序保證至關重要。已釋出的記錄通常對應於某些現實事件,因此保留這些事件的時間表通常很重要。

一個主題Topic是分割槽的邏輯集合。一個主題可以具有一個或多個分割槽,並且一個分割槽必須恰好是一個主題的一部分。由於主題內的分割槽是相互獨立的,因此稱該主題展現出部分順序。

簡單來說,這意味著某些記錄可以相對於彼此排序,而相對於某些其他記錄則不排序。總順序和部分順序的概念雖然聽起來有些學術性,但在構建效能事件流管道中非常重要。它使我們能夠處理記錄並行。

  

生產者角色

瞭解了主題和分割槽的概念之後,您可能想知道記錄如何對映到基礎流。

生產者在釋出記錄時指定分割槽,假設您要釋出到具有多個分割槽的主題。(可能只有一個分割槽主題,在這種情況下這不是問題。)可以直接(通過指定分割槽索引)來實現,也可以間接地通過確定性地雜湊為一致的記錄鍵,(即每次相同)分割槽索引來實現。  

生產者可以在釋出記錄時顯式分配分割槽索引,儘管這種方法很少使用。一種更常見的方法是將金鑰分配給記錄。金鑰對Kafka完全不透明-換句話說,Kafka不會嘗試解釋金鑰的內容,而是將其視為位元組陣列。使用一致的雜湊技術對這些位元組進行雜湊處理以得出分割槽索引。

共享相同雜湊的記錄可以保證佔據相同的分割槽。。假設一個主題具有多個分割槽,則具有不同鍵的記錄可能最終會位於不同的分割槽中。但是,由於雜湊衝突,具有不同雜湊值的記錄也可能最終會在同一分割槽中。這就是雜湊的本質。

生產者很少在乎記錄將對映到哪個特定分割槽,只有相關記錄最終在同一分割槽中並且保留其順序。同樣,使用者對分配的分割槽也無動於衷,只要它們以與釋出時相同的順序接收記錄,並且其分割槽分配不會與組中的其他使用者重疊。

  

消費組

記錄的實際處理是由消費者(在(可選)消費者組內)進行的。後者充當負載平衡機制-在組內的各個使用者例項之間大致均勻地分配分割槽分配。Kafka保證一個分割槽最多隻能分配給其消費者組中的一個消費者。(我們說“至多”是為了涵蓋所有使用者都處於離線狀態的情況。)當組中的第一個使用者訂閱該主題時,它將收到該主題中的所有分割槽。當第二個使用者隨後加入時,它將獲得大約一半的分割槽,從而使第一個使用者減輕了先前負載的一半。這使您能夠並行處理事件流,並根據需要新增使用者(理想情況下,使用自動縮放機制),前提是您已對事件流進行了充分的分割槽。

使用開源Kafdrop工具拍攝:顯示了一個具有16個分割槽的真實主題。“消費者偏移”列顯示一個特定消費者組在每個分割槽內的偏移,它完全獨立於可能訂閱同一主題的其他消費者組的偏移。

為了提高消費者的吞吐量,必須考慮兩個單獨的因素:

  1. 主題分割槽方案。應該對主題進行分割槽,以使獨立事件子流的數量最大化。換句話說,僅在絕對必要的情況下才應保留記錄順序。如果任何兩個記錄在因果關係上均不合法相關,則不應將它們繫結到同一分割槽。這意味著要使用不同的鍵,因為Kafka將使用記錄的鍵作為雜湊源來匯出其一致的分割槽對映。
  2. 組中的消費者數量。您可以增加使用者數量以匹配入站記錄的負載,最多可以達到主題中的分割槽數量。(如果願意,您可以有更多的使用者,但是分割槽數將為至少獲得一個分割槽分配的活動使用者的數量設定上限;其餘使用者將保持空閒狀態。)如果您有執行管道的需要,在雲環境中,理想情況下,應啟用例項自動擴充套件功能,以根據需要增加使用者數量。請注意,使用者可以是程式或執行緒。根據使用者執行的工作負載型別,您可以使用多個單獨的使用者執行緒,或處理執行緒池中的記錄。

 

結論

我們已經證明,可以在同時中合理地使用順序和並行性,而不會破壞物理定律。訣竅在於“順序”的定義,特別是總順序和部分順序之間的區別。一個人不能在不擦除順序的情況下並行處理全部排序的記錄。但是,當部分排序的流分解為幾個不相關的完全排序的子流時,我們可以輕鬆地並行處理後者。

使用Kafka的基本構建塊(主題,分割槽和使用者組),您可以構建高效能的事件流應用程式。

 

相關文章