kafka訊息傳遞語義

現在我們對生產者和消費者的工作方式有了一些瞭解,讓我們來討論 Kafka 在生產者和消費者之間提供的語義保證。 顯然,可以提供多種可能的訊息傳遞保證:

最多一次——訊息可能會丟失,但永遠不會重新發送。

至少一次——訊息永遠不會丟失,但可能會重新發送。

恰好一次——這是人們真正想要的,每條訊息只傳遞一次。

值得注意的是,這分為兩個問題:釋出訊息的永續性保證和消費訊息時的保證。

許多系統聲稱提供“恰好一次”交付語義,但閱讀細則很重要,這些宣告中的大多數是誤導性的(即它們沒有轉化為消費者或生產者可能失敗的情況,存在多個 消費者程序,或寫入磁碟的資料可能丟失的情況)。

Kafka 的語義是直截了當的。 當釋出訊息時,我們有一個訊息被“提交”到日誌的概念。 一旦提交了已釋出的訊息,只要複製該訊息所寫入分割槽的broker保持“活動”,它就不會丟失。 已提交訊息的定義、活動分割槽以及我們嘗試處理的故障型別的描述將在下一節中更詳細地描述。 現在讓我們假設一個完美的無損broker,並嘗試瞭解對生產者和消費者的保證。 如果生產者嘗試釋出訊息並遇到網路錯誤,則無法確定此錯誤是發生在訊息提交之前還是之後。 這類似於使用自動生成的鍵插入資料庫表的語義。

在 0。11。0。0 之前,如果生產者未能收到表明訊息已提交的響應,則它別無選擇,只能重新發送訊息。 這提供了至少一次傳遞語義,因為如果原始請求實際上已經成功,則訊息可能會在重新發送期間再次寫入日誌。 從 0。11。0。0 開始,Kafka 生產者還支援冪等傳遞選項,以保證重新發送不會導致日誌中出現重複條目。 為此,broker為每個生產者分配一個 ID,並使用生產者隨每條訊息傳送的序列號對訊息進行重複資料刪除。 同樣從 0。11。0。0 開始,生產者支援使用類似事務的語義將訊息傳送到多個主題分割槽的能力:即所有訊息都已成功寫入或沒有訊息寫入成功。 主要用例是 Kafka 主題之間的恰好一次處理(如下所述)。

並非所有用例都需要如此強大的保證。 對於延遲敏感的用途,我們允許生產者指定其所需的永續性級別。 如果生產者指定它要等待正在提交的訊息,則這可能需要 10 毫秒的時間。 然而,生產者也可以指定它想要完全非同步地執行傳送,或者它只想等到領導者(但不一定是追隨者)收到訊息。

現在讓我們從消費者的角度來描述語義。 所有副本都具有完全相同的日誌和相同的偏移量。 消費者控制其在此日誌中的位置。 如果消費者從未崩潰,它可以只將這個位置儲存在記憶體中,但是如果消費者失敗並且我們希望這個主題分割槽被另一個程序接管,新程序將需要選擇一個合適的位置開始處理。 假設消費者讀取一些訊息——它有幾個選項來處理訊息和更新其位置。

它可以讀取訊息,然後將其位置儲存在日誌中,最後處理訊息。 在這種情況下,消費者程序有可能在儲存其位置之後但在儲存其訊息處理的輸出之前崩潰。 在這種情況下,接管處理的程序將從儲存的位置開始,即使該位置之前的一些訊息尚未處理。 這對應於“最多一次”語義,因為在消費者失敗訊息的情況下可能不會被處理。

它可以讀取訊息,處理訊息,並最終儲存其位置。 在這種情況下,消費者程序有可能在處理訊息之後但在儲存其位置之前崩潰。 在這種情況下,當新程序接管它收到的前幾條訊息時,它已經被處理了。 在消費者失敗的情況下,這對應於“至少一次”語義。 在許多情況下,訊息有一個主鍵,因此更新是冪等的(兩次接收相同的訊息只會用它自己的另一個副本覆蓋一條記錄)。

那麼恰好一次語義(即你真正想要的東西)呢? 當從 Kafka 主題消費並生產到另一個主題時(如在 Kafka Streams 應用程式中),我們可以利用上面提到的 0。11。0。0 中新的事務性生產者功能。 消費者的位置作為訊息儲存在主題中,因此我們可以在與接收處理資料的輸出主題相同的事務中將偏移量寫入 Kafka。 如果交易被中止,消費者的位置將恢復到其舊值,並且其他消費者將無法看到輸出主題上產生的資料,這取決於他們的“隔離級別”。 在預設的“read_uncommitted”隔離級別中,所有訊息對消費者都是可見的,即使它們是中止事務的一部分,但在“read_committed”中,消費者只會返回來自已提交事務的訊息(以及任何不屬於該事務的訊息) 交易)。

寫入外部系統時,限制在於需要協調消費者的位置與實際儲存為輸出的內容。 實現這一點的經典方法是在消費者位置的儲存和消費者輸出的儲存之間引入兩階段提交。 但這可以透過讓消費者將其偏移量儲存在與其輸出相同的位置來更簡單、更一般地處理。 這更好,因為消費者可能想要寫入的許多輸出系統不支援兩階段提交。 舉個例子,考慮一個 Kafka Connect 聯結器,它在 HDFS 中填充資料以及它讀取的資料的偏移量,以便保證資料和偏移量都被更新,或者都不更新。 對於需要這些更強語義並且訊息沒有允許重複資料刪除的主鍵的許多其他資料系統,我們遵循類似的模式。

因此,Kafka 有效地支援 Kafka Streams 中的一次性交付,並且在 Kafka 主題之間傳輸和處理資料時,通常可以使用事務性生產者/消費者來提供一次性交付。 其他目標系統的 Exactly-once 交付通常需要與此類系統合作,但 Kafka 提供了使實現這一點可行的偏移量(另見 Kafka Connect)。 否則,Kafka 預設保證至少一次交付,並允許使用者透過在處理一批訊息之前禁用對生產者的重試和在消費者中提交偏移量來實現至少一次交付。