面試官問:Kafka 會不會丟訊息?怎麼處理的?

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

面試官:今天我想問下,你覺得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相關的資訊)的對映關係,透過msgId用來獲取相關元資訊

候選者:五、當業務處理完訊息後,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消費),又能解決大部分消費順序的問題了呢。

面試官問:Kafka 會不會丟訊息?怎麼處理的?

面試官:嗯…懂了

Java開源專案推薦

我推薦一個擁有從零開始的文件的Java開源專案,既能用於畢業設計,又可以用在面試。已經有不少的同學透過這個專案拿到了大廠的offer啦(美團/vivo/阿里等等)

該專案業務極容易理解,程式碼結構是比較清晰的,最可怕的是幾乎每個方法和每個類都帶有中文註釋,並且程式碼完全透過阿里開發外掛檢查。

擁有非常全的文件,作者從零搭建的過程都有詳細地記錄,專案裡使用了蠻多的可靠和穩定的中介軟體的。在使用每一個技術棧之前作者都講述了為什麼要使用,以及它的業務背景。我看過,他所說的場景是完全貼合線上環境的

我感覺這個專案就是奔著真實網際網路線上專案去設計和實現的,將專案克隆下來把中介軟體換成目前公司在用的,配合自身的需求完善下基礎建設,它就能線上上執行了。

我跟著README文件的部署使用姿勢很快就能跑起來,最少只需要依賴MySQL和Redis。作者還搞了個前端功能介面,這就讓系統變得更好理解了。而且,在GitHub或者Gitee所提的Issue幾乎都會有回覆,看出來也非常樂於合併開發者們的pull request,會讓人參與感賊強

我相信在校、工作一年左右或常年做內網CRUD後臺的同學去看看肯定會有所啟發,作者也會經常在群裡回答該專案相關的問題和程式碼設計思路。

專案裡會應用到各種設計模式(我稍微看了下,應該有7~8種吧),用到了各種的好用的工具元件,動態執行緒池、日誌切面元件之類,都是主流的技術棧...目前這個專案GitHub和Gitee加起來已經 9K stars了,我相信破萬是遲早的事情。

相關文章