“田由甲” - Kafka重複消費線上問題暴雷

帝莘發表於2024-03-10

  Kafka作為一款高效能、分散式的訊息佇列系統,在大資料領域被廣泛應用。然而,在使用Kafka時,重複消費問題是一個常見的挑戰,可能會對系統的資料一致性和業務邏輯造成影響。我知道Kafka這個名詞時還是在2019年剛工作的時候,但那時候公司使用的訊息佇列體量很小,所以只用了activeMq,我沒再繼續深入研究kafka相關;2021年下半年時,去了新公司,新公司規模大,數量量也巨大,許多系統之間要進行資料傳輸,我們用的訊息中介軟體就是Kafka,於是拜讀了一些Kafka介紹,看了一兩個影片,看了下前輩們在程式碼中怎麼使用的Kafka,裝模做樣的用起來了,也倒也沒有發生什麼問題,23年3月份某天晚上,我們上線了一個嘔心瀝血做了半年的專案,那個專案從服務構建、資料庫設計、核心功能開發、與上下游系統的溝通互動我都深深的參與其中,結果上線當晚倒也沒啥,第二天下午時,我們的下游系統同事打來電話了,問我們系統被攻擊了麼,怎麼有個叫“田由甲”的使用者資訊一直在拋送呢。此刻問題丟我這了,畢竟和下游系統互動這塊一直是我負責的,我立馬開啟了服務日誌,卻發現從凌晨3點到下午之間的日誌一直被田由甲霸屏了,整個日誌訊息都是“使用者田由甲修改了個人資訊,value:{......},partition:{......},offset=XXXXX”。

我知道,現在這個問題丟給如今的我來看肯定很快能解決掉的,但對於當時的我來講確實有一點懵,因為確實沒遇到過Kafka重複消費的問題,我看遍日誌也沒發現列印異常的地方,而且每次修改完資訊後,資料也同步給下游系統了,這個功能上線前測了無數次,怎麼一上線偏偏一直在這列印資料呢。當時的腦子閃過一個念頭,該不會我們的使用者系統真的被攻擊了,有人在定時刷我們的介面,一直修改這個田由甲的資料吧。當時的我們立刻找個田大哥的手機號,打過去詢問了下,結果田大哥告訴我們他確實凌晨登了一次系統,只不過就修改了下暱稱,之後沒再登過了。看來還得回到問題本身,想想為什麼測試環境沒有重複消費,而生產一直消費個沒完了呢。在分析問題之前,先給大家簡單介紹下我們的資料流向:

我們的系統很大,裡面有幾十個微服務,有一個使用者中心,它對應的是一個單獨的使用者服務,所有全量的使用者資料在這裡都能找的到,每個業務服務呢都是處理不同的業務場景的,服務自己面對的客戶群體也不一樣,可以說每個業務服務這裡也需要用到一份屬於自己客戶群體的客戶資料,在自己的資料庫中留存以方便邏輯處理和使用,每當使用者在使用者中心增加修改刪除時,使用者服務會把資料丟到一個公共的topic中,我們權且叫做topicA,三個業務服務去訂閱這個topicA,根據type的不同選擇來消費資料進行落庫更新處理,我新上線的這個叫做服務三吧,它和下游系統的APP有資料上的互動,我從使用者系統接到資料變化後,進行落庫,然後資料業務加工後會推送到topicB中,下游系統訂閱了這個topicB,接收到資料再更新自己的使用者資訊。

  問題回到田由甲身上,首先使用者說只修改了一次,而且我們公司有自己的安全部門,被這樣攻擊近10個小時不太現實,而且業務系統一監聽田由甲也沒發生問題,那問題肯定不在使用者服務的資料跑送上,必然是我們新上線的服務出現了問題。我靜下心來仔細觀察了下日誌,發現日誌上的offset都是同一個值,說明程式執行完之後,偏移量沒有被正確的提交,導致一直在消費同一條資料,但是另一個問題來了,既然下游APP接到了資料,說明方法邏輯肯定都走完了,排除發生異常的可能,那為何偏移量還是提交不上呢,於是又排查了下我寫的Kafka消費和別人寫的區別,我發現新服務的Kafka配置我開啟了自動提交,而老服務別人是使用的手動提交方法,突然有了想法,手動提交是邏輯執行完之後執行ack.acknowledeg(),而自動提交則和配置的提交時間有關,如果方法執行超過了配置的時間閾值,那麼偏移量將提交不上去。想到這,抓緊先把偏移量自動提交修改為了手動提交,將程式碼重新部署下。

  其實這裡還有個問題沒有根本解決,為什麼配置自動提交測試環境一直沒有出現重複消費的問題呢,那段處理邏輯程式碼並不複雜,怎麼會執行時間超出閾值設定的幾十秒呢?我仔細想了下,一般程式碼慢都會和資料庫互動有關,大多情況發生在查詢上,而我寫的這段邏輯只有一個地方透過手機號查使用者資訊和DB進行查詢方面的互動了,想到這,我立馬意識到一個問題,這張表並不是我新建的,我只是在這張使用者表加了幾個欄位以便於我新業務新服務的使用,這個表生產一直有,所以說它和測試環境最大區別那就是資料量,雖然我參與的這部分使用者群體只是整個系統總的使用者群體的一個子集,只佔很小的一部分,那在生產環境上,也肯定是有幾十萬條資料的,突然有個大膽的想法,該不會這張user表前輩們沒有加索引吧!我立馬登上生產庫檢視,好傢伙,這張使用者表,果然一個索引都沒建,一個mysql表,幾十萬條資料,沒有索引,直接用手機號查,那肯定是全表掃描,不慢才怪。。。測試環境沒有那麼大的資料量,必然測不出來啊,於是立馬給使用者表的手機號欄位建了索引。

  當時的我由於對Kafka重複消費問題第一次遇到,確實解決它廢了點心思,最近這一年裡,參與的專案幾乎都使用到了Kafka,又遇到了好多次Kafka消費慢等最佳化的需求,愈加熟練的去解決了,接下來我將言歸正傳,官方的陳述下Kafka重複消費問題的根本原因、常見場景以及解決方法,希望能夠幫助讀者更好地理解和應對這一問題。

1. 重複消費問題的根本原因

Kafka的消費者組模型是保證高吞吐量和水平擴充套件性的關鍵,但也為重複消費問題埋下了隱患。消費者組中的每個消費者會負責消費特定分割槽的訊息,並維護自身的消費偏移量(offset),用於記錄已經消費的訊息位置。然而,如果消費者在處理訊息時發生故障或者處理時間過長,可能會導致消費偏移量未能及時提交,從而造成訊息被重複消費的情況。

此外,Kafka本身並不保證訊息的順序性,如果訊息在生產者端重試傳送或者在網路傳輸過程中發生重複,也會導致訊息被重複消費的情況發生。因此,要解決重複消費問題,需要從多個方面進行考慮和處理。

2. 常見場景下的重複消費問題

2.1. 消費者故障重啟

當消費者由於故障而重啟時,可能會導致消費偏移量丟失或者未能及時提交,從而造成訊息被重複消費。

2.2. 消費者處理時間過長

如果消費者處理訊息的時間過長,可能會導致在訊息處理完成前發生故障或重啟,從而引發重複消費問題。

2.3. 訊息重試或網路傳輸問題

在訊息生產和傳輸過程中,如果訊息發生重試傳送或者在網路傳輸過程中發生重複,也會導致訊息被重複消費的情況發生。

3. 解決方法

3.1. 使用冪等性處理

在消費者端實現冪等性處理是解決重複消費問題的有效方法。透過在消費端記錄已經處理過的訊息ID或者透過訊息去重的方式,可以保證同一訊息不會被重複處理。

3.2. 提交消費偏移量

及時提交消費偏移量是避免重複消費的關鍵。可以透過設定適當的提交頻率或者在訊息處理完成後手動提交偏移量來確保消費偏移量的準確性。

3.3. 使用Exactly-Once語義

Kafka提供了Exactly-Once語義來解決重複消費和訊息丟失的問題,可以透過配置事務或者冪等性生產者來實現Exactly-Once語義。

4. 結語

在使用Kafka時,重複消費問題是一個需要重視的挑戰,但透過合理的設計和實施解決方案,可以有效地避免和解決重複消費帶來的影響。希望本文的內容能夠幫助讀者更好地理解Kafka重複消費問題並採取相應的措施,確保系統的資料一致性和穩定性。

5. 參考資料

  1. Kafka官方文件: https://kafka.apache.org/documentation/
  2. Confluent官方文件: https://docs.confluent.io/

相關文章