kafka 如何保證不重複消費又不丟失資料?

牌面宝宝發表於2024-06-20
作者:Java3y
連結:https://www.zhihu.com/question/483747691/answer/2392949203
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

面試官:今天我想問下,你覺得Kafka會丟資料嗎?

候選者:嗯,使用Kafka時,有可能會有以下場景會丟訊息

候選者:比如說,我們用Producer發訊息至Broker的時候,就有可能會丟訊息

候選者:如果你不想丟訊息,那在傳送訊息的時候,需要選擇帶有 callBack的api進行傳送

候選者:其實就意味著,如果你傳送成功了,會回撥告訴你已經傳送成功了。如果失敗了,那收到回撥之後自己在業務上做重試就好了。

候選者:等到把訊息傳送到Broker以後,也有可能丟訊息

候選者:一般我們的線上環境都是叢集環境下嘛,但可能你傳送的訊息後broker就掛了,這時掛掉的broker還沒來得及把資料同步給別的broker,資料就自然就丟了

候選者:傳送到Broker之後,也不能保證資料就一定不丟了,畢竟Broker會把資料儲存到磁碟之前,走的是作業系統快取

候選者:也就是非同步刷盤這個過程還有可能導致資料會丟

kafka 如何保證不重複消費又不丟失資料?

候選者:嗯,到這裡其實我已經說了三個場景了,分別是:producer -> broker ,broker->broker之間同步,以及broker->磁碟

候選者:要解決上面所講的問題也比較簡單,這塊也沒什麼好說的…

候選者:不想丟資料,那就使用帶有callback的api,設定 acks、retries、factor等等些引數來保證Producer傳送的訊息不會丟就好啦。

面試官:嗯…

候選者:一般來說,還是client 消費 broker 丟訊息的場景比較多

面試官那你們在消費資料的時候是怎麼保證資料的可靠性的呢?

候選者:首先,要想client端消費資料不能丟,肯定是不能使用autoCommit的,所以必須是手動提交的。

kafka 如何保證不重複消費又不丟失資料?

候選者:我們這邊是這樣實現的:

候選者:一、從Kafka拉取訊息(一次批次拉取500條,這裡主要看配置)時

候選者:二、為每條拉取的訊息分配一個msgId(遞增)

候選者:三、將msgId存入記憶體佇列(sortSet)中

候選者:四、使用Map儲存msgId與msg(有offset相關的資訊)的對映關係

候選者:五、當業務處理完訊息後,ack時,獲取當前處理的訊息msgId,然後從sortSet刪除該msgId(此時代表已經處理過了)

候選者:六、接著與sortSet佇列的首部第一個Id比較(其實就是最小的msgId),如果當前msgId<=sort Set第一個ID,則提交當前offset

候選者:七、系統即便掛了,在下次重啟時就會從sortSet隊首的訊息開始拉取,實現至少處理一次語義

候選者:八、會有少量的訊息重複,但只要下游做好冪等就OK了。

kafka 如何保證不重複消費又不丟失資料?

面試官:嗯,你也提到了冪等,你們這業務怎麼實現冪等性的呢?

候選者:嗯,還是以處理訂單訊息為例好了。

候選者:冪等Key我們由訂單編號+訂單狀態所組成(一筆訂單的狀態只會處理一次)

候選者:在處理之前,我們首先會去查Redis是否存在該Key,如果存在,則說明我們已經處理過了,直接丟掉

候選者:如果Redis沒處理過,則繼續往下處理,最終的邏輯是將處理過的資料插入到業務DB上,再到最後把冪等Key插入到Redis上

候選者:顯然,單純透過Redis是無法保證冪等的(:

候選者:所以,Redis其實只是一個「前置」處理,最終的冪等性是依賴資料庫的唯一Key來保證的(唯一Key實際上也是訂單編號+狀態)

候選者:總的來說,就是透過Redis做前置處理,DB唯一索引做最終保證來實現冪等性的

kafka 如何保證不重複消費又不丟失資料?

面試官你們那邊遇到過順序消費的問題嗎?

候選者:嗯,也是有的,我舉個例子

候選者:訂單的狀態比如有 支付、確認收貨、完成等等,而訂單下還有計費、退款的訊息報

候選者:理論上來說,支付的訊息報肯定要比退款訊息報先到嘛,但程式處理的過程中可不一定的嘛

候選者:所以在這邊也是有消費順序的問題

候選者:但在廣告場景下不是「強順序」的,只要保證最終一致性就好了。

候選者:所以我們這邊處理「亂序」訊息的實現是這樣的:

候選者:一、寬表:將每一個訂單狀態,單獨分出一個或多個獨立的欄位。訊息來時只更新對應的欄位就好,訊息只會存在短暫的狀態不一致問題,但是狀態最終是一致的

候選者:二、訊息補償機制:另一個進行消費相同topic的資料,訊息落盤,延遲處理。將訊息與DB進行對比,如果發現資料不一致,再重新傳送訊息至主程序處理

候選者:還有部分場景,可能我們只需要把相同userId/orderId傳送到相同的partition(因為一個partition由一個Consumer消費),又能解決大部分消費順序的問題了呢。

相關文章