記Kafka消費的一次生產(chǎn)故障處理過程
大家好,歡迎來到Tlog4J課堂,我是Jensen。
記錄今天發(fā)生的一次生產(chǎn)故障以及故障處理全過程。
問題背景
需求背景是這樣的:產(chǎn)品要求訂單過售后期后,資金平臺需要對這些訂單進行結算,并把虛擬資產(chǎn)入賬到下單客戶的虛擬賬戶。
因為我們是按業(yè)務領域拆分多個微服務的,為了解耦訂單與資金平臺,我們選擇了MQ異步消息的方式進行業(yè)務數(shù)據(jù)傳遞,流程簡化如下:
- 【訂單中心】查詢已過售后訂單 -> 發(fā)送MQ消息給財務中心。
- 【財務中心】接收MQ消息 -> 校驗客戶交易數(shù)據(jù) -> 調(diào)用資金平臺結算積分。
- 【資金平臺】結算積分 -> 虛擬資產(chǎn)入賬。
其中,財務中心MQ消費使用了一個基于Kafka二次封裝的組件,默認通過應用內(nèi)線程池異步消費消息進行業(yè)務處理(因為需要在多個地方消費),這個二開的組件也已經(jīng)用了一年時間,相對較為穩(wěn)定。
OK,到這一步?jīng)]有發(fā)現(xiàn)什么問題。
接下來,不出意外的話馬上就會發(fā)生意外。
凌晨6點觸發(fā)P1級告警,由于應用內(nèi)線程池被撐爆,應用走拒絕策略737次,觸發(fā)SQL慢查詢持續(xù)10秒(剛好校驗客戶交易數(shù)據(jù)操作用到了非索引列查數(shù)據(jù)庫)。
隨后進行了問題排查,分析完生產(chǎn)者、消費者端的代碼,發(fā)現(xiàn)有以下問題:
- 消費端財務中心對應的消費方法使用了默認的異步方式處理消息,線程數(shù)大小用了默認的200個線程,如果短時間內(nèi)接收多條MQ而又無法快速執(zhí)行完釋放線程,線程數(shù)達到200個必然會走拒絕策略報錯,甚至影響其它異步執(zhí)行MQ的消費者方法(共用了同一個線程池)。
- 訂單中心同一時刻批量修改已過售后訂單,把發(fā)送MQ的方法包在了for循環(huán)中。這意味著如果同一時刻發(fā)送大量MQ消息,又因為第一條消費者存在的隱患,將導致發(fā)送的MQ消息無法被正常消費。
處理過程
分析完問題,基本上能確定如何解決了,分三步:
第一步:對于線上消費異常的數(shù)據(jù),按照代碼邏輯重新跑SQL修復相應數(shù)據(jù)。這件事需要第一時間做,不能因為程序的問題影響客戶體驗。
第二步:該MQ組件異步消費的消息堆積能力受線程池大小影響,應該把消息堆積的問題交給專業(yè)的MQ自己負責,所以暫時關掉該Topic的異步執(zhí)行,不用線程池,改為同步。后續(xù)對該MQ組件進行優(yōu)化,不再提供異步執(zhí)行方式,如使用類似@KafkaListener(topic = "xxx", groupId = "
appName.beanName.methodName")的方式,只不過需要動態(tài)創(chuàng)建KafkaListener,利用MQ本身消費者組的功能,避免消息堆積在應用線程池內(nèi)。
第三步:通過業(yè)務規(guī)避,合理評估需求,對于已經(jīng)確定的場景,能合并的MQ請求、SQL請求、Feign接口調(diào)用請求,比如上面提到的for循環(huán)發(fā)送訂單已過售后通知、校驗客戶交易數(shù)據(jù)、資金平臺積分入賬場景,把它們識別出來,通過批量合并請求的方式解決頻繁請求可能發(fā)生的問題(以空間換頻率)。請求合并后還需要評估合并請求的大小限制,進一步進行請求切割,比如訂單合并后有10萬條數(shù)據(jù),放在一個請求里也不合理,應該按照一定的訂單量切割后再發(fā)送請求。
這里分享一個領導(技術總監(jiān))用了8年的需求分析方法:
總結一下
對于此類生產(chǎn)問題,分三步解決:
- 第一,第一時間修復生產(chǎn)數(shù)據(jù),避免影響客戶體驗。
- 第二,找出臨時解決方案——找出問題根因針對性解決。
- 第三,長遠的問題規(guī)避方案——合理評估需求。