目錄
前言:
冪等
事務
總結:?
參考資料?
前言:
Kafka 消息交付可靠性保障以及精確處理一次語義的實現。
所謂的消息交付可靠性保障,是指 Kafka 對 Producer 和 Consumer 要處理的消息提供什么樣的承諾。常見的承諾有以下三種:
- 最多一次(at most once):消息可能會丟失,但絕不會被重復發送。
- 至少一次(at least once):消息不會丟失,但有可能被重復發送。
- 精確一次(exactly once):消息不會丟失,也不會被重復發送。
目前,Kafka 默認提供的交付可靠性保障是第二種,即至少一次。?
? ?即只有 Broker 成功“提交”消息且 Producer 接到 Broker 的應答才會認為該消息成功發送。不過倘若消息成功“提交”,但 Broker 的應答沒有成功發送回 Producer 端(比如網絡出現瞬時抖動),那么 Producer 就無法確定消息是否真的提交成功了。因此,它只能選擇重試,也就是再次發送相同的消息。這就是 Kafka 默認提供至少一次可靠性保障的原因,不過這會導致消息重復發送。
大部分用戶還是希望消息只會被交付一次,這樣的話,消息既不會丟失,也不會被重復處理。或者說,即使 Producer 端重復發送了相同的消息,Broker 端也能做到自動去重。在下游 Consumer 看來,消息依然只有一條。?
? ? ? Kafka 是怎么做到精確一次的呢?簡單來說,這是通過兩種機制:冪等性(Idempotence)和事務(Transaction)。
冪等
? ?“冪等”這個詞原是數學領域中的概念,指的是某些操作或函數能夠被執行多次,但每次得到的結果都是不變的。
? ? 冪等性有很多好處,其最大的優勢在于我們可以安全地重試任何冪等性操作,反正它們也不會破壞我們的系統狀態。如果是非冪等性操作,我們還需要擔心某些操作執行多次對狀態的影響,但對于冪等性操作而言,我們根本無需擔心此事。
? ? ? ? 在 Kafka 中,Producer 默認不是冪等性的,但我們可以創建冪等性 Producer。它其實是 0.11.0.0 版本引入的新功能。在此之前,Kafka 向分區發送數據時,可能會出現同一條消息被發送了多次,導致消息重復的情況。在 0.11 之后,指定 Producer 冪等性的方法很簡單,僅需要設置一個參數即可,即 props.put(“enable.idempotence”, ture),或 props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true)。
? ? ? ?enable.idempotence 被設置成 true 后,Producer 自動升級成冪等性 Producer,其他所有的代碼邏輯都不需要改變。Kafka 自動幫你做消息的重復去重。底層具體的原理很簡單,就是經典的用空間去換時間的優化思路,即在 Broker 端多保存一些字段。當 Producer 發送了具有相同字段值的消息后,Broker 能夠自動知曉這些消息已經重復了,于是可以在后臺默默地把它們“丟棄”掉。當然,實際的實現原理并沒有這么簡單,但你大致可以這么理解。
? ? ? 看上去,冪等性 Producer 的功能很酷,使用起來也很簡單,僅僅設置一個參數就能保證消息不重復了,但實際上,我們必須要了解冪等性 Producer 的作用范圍。
? ? ? 首先,它只能保證單分區上的冪等性,即一個冪等性 Producer 能夠保證某個主題的一個分區上不出現重復消息,它無法實現多個分區的冪等性。其次,它只能實現單會話上的冪等性,不能實現跨會話的冪等性。這里的會話,你可以理解為 Producer 進程的一次運行。當你重啟了 Producer 進程之后,這種冪等性保證就喪失了。
? ? ??那么你可能會問,如果我想實現多分區以及多會話上的消息無重復,應該怎么做呢?答案就是事務(transaction)或者依賴事務型 Producer。這也是冪等性 Producer 和事務型 Producer 的最大區別!
事務
?Kafka 的事務概念類似于我們熟知的數據庫提供的事務。在數據庫領域,事務提供的安全性保障是經典的 ACID,即原子性(Atomicity)、一致性 (Consistency)、隔離性 (Isolation) 和持久性 (Durability)。
各大主流數據庫廠商都比較統一。所謂的 read committed,指的是當讀取數據庫時,你只能看到已提交的數據,即無臟讀。同時,當寫入數據庫時,你也只能覆蓋掉已提交的數據,即無臟寫。
Kafka 自 0.11 版本開始也提供了對事務的支持,目前主要是在 read committed 隔離級別上做事情。它能保證多條消息原子性地寫入到目標分區,同時也能保證 Consumer 只能看到事務成功提交的消息。下面我們就來看看 Kafka 中的事務型 Producer。
事務型 Producer 能夠保證將消息原子性地寫入到多個分區中。這批消息要么全部寫入成功,要么全部失敗。另外,事務型 Producer 也不懼進程的重啟。Producer 重啟回來后,Kafka 依然保證它們發送消息的精確一次處理。
設置事務型 Producer 的方法也很簡單,滿足兩個要求即可:
- 和冪等性 Producer 一樣,開啟 enable.idempotence = true。
- 設置 Producer 端參數 transactional. id。最好為其設置一個有意義的名字
此外,你還需要在 Producer 代碼中做一些調整,如這段代碼所示:?
producer.initTransactions();
try {producer.beginTransaction();producer.send(record1);producer.send(record2);producer.commitTransaction();
} catch (KafkaException e) {producer.abortTransaction();
}
nitTransaction、beginTransaction、commitTransaction 和 abortTransaction,它們分別對應事務的初始化、事務開始、事務提交以及事務終止。
這段代碼能夠保證 Record1 和 Record2 被當作一個事務統一提交到 Kafka,要么它們全部提交成功,要么全部寫入失敗。實際上即使寫入失敗,Kafka 也會把它們寫入到底層的日志中,也就是說 Consumer 還是會看到這些消息。因此在 Consumer 端,讀取事務型 Producer 發送的消息也是需要一些變更的。修改起來也很簡單,設置 isolation.level 參數的值即可。當前這個參數有兩個取值:
- read_uncommitted:這是默認值,表明 Consumer 能夠讀取到 Kafka 寫入的任何消息,不論事務型 Producer 提交事務還是終止事務,其寫入的消息都可以讀取。很顯然,如果你用了事務型 Producer,那么對應的 Consumer 就不要使用這個值。
- read_committed:表明 Consumer 只會讀取事務型 Producer 成功提交事務寫入的消息。當然了,它也能看到非事務型 Producer 寫入的所有消息。
總結:?
冪等性 Producer 和事務型 Producer 都是 Kafka 社區力圖為 Kafka 實現精確一次處理語義所提供的工具,只是它們的作用范圍是不同的。冪等性 Producer 只能保證單分區、單會話上的消息冪等性;而事務能夠保證跨分區、跨會話間的冪等性。從交付語義上來看,自然是事務型 Producer 能做的更多。
不過,切記天下沒有免費的午餐。比起冪等性 Producer,事務型 Producer 的性能要更差,在實際使用過程中,我們需要仔細評估引入事務的開銷,切不可無腦地啟用事務。
參考資料?
14 | 冪等生產者和事務生產者是一回事嗎?-極客時間