java消息分发_Kafka教程设计思想之消息分发策略
現在我們對于 producer 和 consumer 的工作原理已將有了一點了解,讓我們接著討論 Kafka 在 producer 和 consumer 之間提供的語義保證。顯然,Kafka可以提供的消息交付語義保證有多種:
At most once——消息可能會丟失但絕不重傳。At least once——消息可以重傳但絕不丟失。Exactly once——這正是人們想要的, 每一條消息只被傳遞一次.值得注意的是,這個問題被分成了兩部分:發布消息的持久性保證和消費消息的保證。
很多系統聲稱提供了“Exactly once”的消息交付語義, 然而閱讀它們的細則很重要, 因為這些聲稱大多數都是誤導性的 (即它們沒有考慮 consumer 或 producer 可能失敗的情況,以及存在多個 consumer 進行處理的情況,或者寫入磁盤的數據可能丟失的情況。).
Kafka 的語義是直截了當的。 發布消息時,我們會有一個消息的概念被“committed”到 log 中。 一旦消息被提交,只要有一個 broker 備份了該消息寫入的 partition,并且保持“alive”狀態,該消息就不會丟失。 有關 committed message 和 alive partition 的定義,以及我們試圖解決的故障類型都將在下一節進行細致描述。
現在讓我們假設存在完美無缺的 broker,然后來試著理解 Kafka 對 producer 和 consumer 的語義保證。如果一個 producer 在試圖發送消息的時候發生了網絡故障, 則不確定網絡錯誤發生在消息提交之前還是之后。這與使用自動生成的鍵插入到數據庫表中的語義場景很相似。
在 0.11.0.0 之前的版本中, 如果 producer 沒有收到表明消息已經被提交的響應, 那么 producer 除了將消息重傳之外別無選擇。 這里提供的是 at-least-once 的消息交付語義,因為如果最初的請求事實上執行成功了,那么重傳過程中該消息就會被再次寫入到 log 當中。
從 0.11.0.0 版本開始,Kafka producer新增了冪等性的傳遞選項,該選項保證重傳不會在 log 中產生重復條目。 為實現這個目的, broker 給每個 producer 都分配了一個 ID ,并且 producer 給每條被發送的消息分配了一個序列號來避免產生重復的消息。
同樣也是從 0.11.0.0 版本開始, producer 新增了使用類似事務性的語義將消息發送到多個 topic partition 的功能: 也就是說,要么所有的消息都被成功的寫入到了 log,要么一個都沒寫進去。這種語義的主要應用場景就是 Kafka topic 之間的 exactly-once 的數據傳遞(如下所述)。
并非所有使用場景都需要這么強的保證。對于延遲敏感的應用場景,我們允許生產者指定它需要的持久性級別。如果 producer 指定了它想要等待消息被提交,則可以使用10ms的量級。然而, producer 也可以指定它想要完全異步地執行發送,或者它只想等待直到 leader 節點擁有該消息(follower 節點有沒有無所謂)。
現在讓我們從 consumer 的視角來描述語義。 所有的副本都有相同的 log 和相同的 offset。consumer 負責控制它在 log 中的位置。如果 consumer 永遠不崩潰,那么它可以將這個位置信息只存儲在內存中。但如果 consumer 發生了故障,我們希望這個 topic partition 被另一個進程接管, 那么新進程需要選擇一個合適的位置開始進行處理。假設 consumer 要讀取一些消息——它有幾個處理消息和更新位置的選項。
Consumer 可以先讀取消息,然后將它的位置保存到 log 中,最后再對消息進行處理。在這種情況下,消費者進程可能會在保存其位置之后,帶還沒有保存消息處理的輸出之前發生崩潰。而在這種情況下,即使在此位置之前的一些消息沒有被處理,接管處理的進程將從保存的位置開始。在 consumer 發生故障的情況下,這對應于“at-most-once”的語義,可能會有消息得不到處理。Consumer 可以先讀取消息,然后處理消息,最后再保存它的位置。在這種情況下,消費者進程可能會在處理了消息之后,但還沒有保存位置之前發生崩潰。而在這種情況下,當新的進程接管后,它最初收到的一部分消息都已經被處理過了。在 consumer 發生故障的情況下,這對應于“at-least-once”的語義。 在許多應用場景中,消息都設有一個主鍵,所以更新操作是冪等的(相同的消息接收兩次時,第二次寫入會覆蓋掉第一次寫入的記錄)。那么 exactly once 語義(即你真正想要的東西)呢?當從一個 kafka topic 中消費并輸出到另一個 topic 時 (正如在一個Kafka Streams 應用中所做的那樣),我們可以使用我們上文提到的 0.11.0.0 版本中的新事務型 producer,并將 consumer 的位置存儲為一個 topic 中的消息,所以我們可以在輸出 topic 接收已經被處理的數據的時候,在同一個事務中向 Kafka 寫入 offset。如果事務被中斷,則消費者的位置將恢復到原來的值,而輸出 topic 上產生的數據對其他消費者是否可見,取決于事務的“隔離級別”。
在默認的“read_uncommitted”隔離級別中,所有消息對 consumer 都是可見的,即使它們是中止的事務的一部分,但是在“read_committed”的隔離級別中,消費者只能訪問已提交的事務中的消息(以及任何不屬于事務的消息)。
在寫入外部系統的應用場景中,限制在于需要在 consumer 的 offset 與實際存儲為輸出的內容間進行協調。解決這一問題的經典方法是在 consumer offset 的存儲和 consumer 的輸出結果的存儲之間引入 two-phase commit。但這可以用更簡單的方法處理,而且通常的做法是讓 consumer 將其 offset 存儲在與其輸出相同的位置。
這也是一種更好的方式,因為大多數 consumer 想寫入的輸出系統都不支持 two-phase commit。舉個例子,Kafka Connect連接器,它將所讀取的數據和數據的 offset 一起寫入到 HDFS,以保證數據和 offset 都被更新,或者兩者都不被更新。 對于其它很多需要這些較強語義,并且沒有主鍵來避免消息重復的數據系統,我們也遵循類似的模式。
因此,事實上 Kafka 在Kafka Streams中支持了exactly-once 的消息交付功能,并且在 topic 之間進行數據傳遞和處理時,通常使用事務型 producer/consumer 提供 exactly-once 的消息交付功能。
到其它目標系統的 exactly-once 的消息交付通常需要與該類系統協作,但 Kafka 提供了 offset,使得這種應用場景的實現變得可行。(詳見 Kafka Connect)。否則,Kafka 默認保證 at-least-once 的消息交付, 并且 Kafka 允許用戶通過禁用 producer 的重傳功能和讓 consumer 在處理一批消息之前提交 offset,來實現 at-most-once 的消息交付。
總結
以上是生活随笔為你收集整理的java消息分发_Kafka教程设计思想之消息分发策略的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java正则表达式 引用_java –
- 下一篇: centos系统中mysql密码_Cen