生產案例:消息中間件 MQ 如何處理消費失敗的消息?

1、消息中間件在生產系統中的使用

下圖是一個非常典型的生產環境的問題,很多公司都會在生產系統裡使用MQ,即消息隊列。

也就是說,一個系統跟另外一個系統之間進行通信的時候,假如系統A希望發送一個消息給系統B,讓它去處理。

但是系統A不關注系統B到底怎麼處理或者有沒有處理好,所以系統A把消息發送給MQ,然後就不管這條消息的“死活”了,接著系統B從MQ裡消費出來處理即可。

至於怎麼處理,是否處理完畢,什麼時候處理,都是系統B的事兒,與系統A無關。

上述過程,可以通過下圖看的很清晰:

生產案例:消息中間件 MQ 如何處理消費失敗的消息?


這樣的一種通信方式,就是所謂的“異步”通信方式

對於系統A來說,只要把消息發給MQ,然後系統B就會異步的去進行處理了,系統A不需要“同步”的等待系統B處理完。

這樣的好處是什麼呢?

兩個字:解耦

系統A要跟系統B通信,但是他不需要關注系統B如何處理的一些細節。我們來舉幾個例子說明:

比如,A不需要關注B什麼時候處理完,這樣假如系統B處理一個消息要耗費10分鐘也不關係統A的事兒。

否則,系統A直接調用系統B的接口,系統B一下子處理了10分鐘怎麼辦?難不成系統A也阻塞等待10分鐘?

再比如,系統A不需要關注系統B處理成功與否,即使系統B處理失敗了,也是系統B自己去考慮這個場景和重新嘗試處理。

否則如果系統調用系統B的接口,萬一處理失敗了報錯了,系統A受到一個調用異常該怎麼處理?

還有,系統A不需要關注系統B是否存活。萬一要是系統B掛掉了,系統A通過MQ來通信也不需要管系統B的“死活”,系統B自己恢復了之後就可以從MQ消費消息再次處理即可。

否則系統A直接調用系統B的接口,萬一系統B掛了,難道系統A還要把消息暫存到數據庫?等待系統B恢復了再給他發過去嗎?

這就是通過MQ進行異步通信,讓兩個系統解耦之後的好處,可以大幅度提升整個大系統的容錯性,增加系統的彈性,而不是處處耦合,一個系統出錯連帶導致其他系統全部出錯。

解耦之後,即使出錯也只是大系統中的一個系統B出錯而已,不影響別人。


2、生產案例:早教盒子APP的發貨

接下來用一個經典的生產案例給大家說說MQ在生產的使用。現在很多早教類的APP,都會提供早教盒子,什麼意思呢?

早教APP提供的核心服務就是三塊:

  1. APP裡的早教視頻課程
  2. 線上微信群的助教答疑指導
  3. 線下送你早教盒子,裡面有很多上課道具

這樣一個媽媽陪伴孩子上早教的過程可能是這樣的:

  • 首先在APP裡看早教視頻課程,孩子看著很感興趣。
  • 接著媽媽從早教盒子裡取出來道具,陪孩子把視頻裡的遊戲和任務都做一遍,讓孩子加深印象
  • 最後每天媽媽會打卡,有助教會來給媽媽進行答疑。

所以說,假設現在我們要在一個早教APP裡購買一個早教課程,他的流程大致如下:

  • 選擇購買早教課程
  • 直接支付
  • 創建訂單
  • 給用戶增加課程權限
  • 通知倉庫準備發早教盒子
  • 通知物流公司去倉庫取早教盒子進行配送。

我們來分析一下每個環節。首先你要是購買一個早教課程,那麼點擊“購買”的按鈕之後,一般直接會跳入一個支付界面。

這個時候,你就可以直接選擇支付了。此時後臺系統一定會通過支付系統跟第三方支付系統進行通信,比如說支付寶、微信之類的,然後等待支付完成。

一旦支付完成,就會在自己內部系統幹兩個事:

  • 第一,給這個用戶id創建一個訂單;
  • 第二,給這個用戶id增加看某個早教視頻課程的權限。

此時用戶其實在“我的訂單”界面就可以看到自己的訂單了,而且在“我的課程”界面,就可以開始看早教課程的視頻了。

下圖展示了這個過程:


生產案例:消息中間件 MQ 如何處理消費失敗的消息?


但是現在問題主要在後面兩個步驟,現在你的訂單系統作為核心入口,他要通知倉庫系統去扣減一個早教盒子的庫存。

同時,還得準備好早教盒子的發貨(比如說提前打包裝箱,準備一些給快遞公司使用的發貨單之類的,需要帖子箱子上)。然後通知第三方物流公司的系統,可以去自己的倉庫取早教盒子發貨了。

這兩個步驟需要涉及到對倉庫系統以及第三方物流公司系統的調用,那麼是採用訂單系統直接同步調用那兩個系統的方式嗎?

恐怕不妥,因為這裡最大的問題就是性能問題可用性問題

舉個例子,假如現在倉庫系統部署在其他地方,因為網絡問題導致性能很差,訪問速度很慢,那麼是不是可能會導致用戶支付之後,等待了幾分鐘都看不到整個流程的完成?

或者要是說第三方物流公司的系統現在要是故障了,暫時無法訪問,那麼會不會導致用戶支付了之後,一直沒有給用戶發貨早教盒子?

所以說,在這裡就應該引入MQ,訂單系統在完成訂單的創建以及課程的分配之後,就可以發送一個消息到MQ,然後有一個專門的倉儲系統負責消費這個消息,接著嘗試去調用獨立倉庫系統通知發貨,以及通知第三方物流系統去配送。

整個過程,如下圖所示:


生產案例:消息中間件 MQ 如何處理消費失敗的消息?


這麼做有什麼好處呢?

好處是顯而易見的,假如現在獨立倉庫系統和第三方物流系統的訪問性能突然變得很差,大不了就是倉儲系統在後面慢慢的跟人家通信等著人家處理完畢好了,對訂單系統是沒影響的。

對於訂單系統而言,創建訂單和分配課程都是速度很快的,然後發送個消息到MQ速度也很快。

這樣一來,用戶看到的就是一兩秒的時間支付就成功了,然後可以查到訂單,看到自己的課程,然後訂單的物流顯示的是“待配送”的狀態。

那麼如果獨立倉庫系統或者第三方物流系統故障了,導致倉儲系統消費到一條訂單消息之後,嘗試進行發貨失敗,也就是對這條消費到的消息處理失敗。這種情況,怎麼處理?

這就是本文最核心的地方了!!!


3、死信隊列的使用:處理失敗的消息

一般生產環境中,如果你有豐富的架構設計經驗,都會在使用MQ的時候設計兩個隊列:一個是核心業務隊列,一個是死信隊列

核心業務隊列,就是比如上面專門用來讓訂單系統發送訂單消息的,然後另外一個死信隊列就是用來處理異常情況的。

之所以我們這篇文章拋出一個面試題,結果先長篇大論說一個生產實踐案例和業務場景,就是因為面試被問到這個問題時,必須要結合你自己的業務實踐經驗來說。

你需要先給面試官說有血有肉的業務系統場景,然後再結合這個場景回答他的問題,因為面試官想聽的就是你真實的實踐經驗。

比如說要是第三方物流系統故障了,此時無法請求,那麼倉儲系統每次消費到一條訂單消息,嘗試通知發貨和配送,都會遇到對方的接口報錯。

此時倉儲系統就可以把這條消息拒絕訪問,或者標誌位處理失敗!注意,這個步驟很重要。

一旦標誌這條消息處理失敗了之後,MQ就會把這條消息轉入提前設置好的一個死信隊列中。

然後你會看到的就是,在第三方物流系統故障期間,所有訂單消息全部處理失敗,全部會轉入死信隊列。

然後你的倉儲系統得專門有一個後臺線程,監控第三方物流系統是否正常,能否請求的,不停的監視。

一旦發現對方恢復正常,這個後臺線程就從死信隊列消費出來處理失敗的訂單,重新執行發貨和配送的通知邏輯。

死信隊列的使用,其實就是MQ在生產實踐中非常重要的一環,也就是架構設計必須要考慮的。

最終的架構圖,如下所示:


生產案例:消息中間件 MQ 如何處理消費失敗的消息?

覺得文章不錯就給小老弟點個關注吧,更多內容陸續奉上。

最後,分享一份面試寶典《Java核心知識點整理.pdf》,覆蓋了JVM、鎖、高併發、反射、Spring原理、微服務、Zookeeper、數據庫、數據結構等等。私信回覆“資料”獲取免費領取方式。


分享到:


相關文章: