天天看點

面試官:MQ 消息丢失、重複、積壓問題,如何解決?

面試官在面試候選人 時,如果發現候選人的履歷中寫了在項目中使用了‍ ‍

MQ 技術(如 Kafka、RabbitMQ、RocketMQ),基本都會抛出一個問題:在使用 MQ 的時候,怎麼確定消息 100% 不丢失?

這個問題在實際工作中很常見,既能考察候選者對于 MQ 中間件技術的掌握程度,又能很好地區分候選人的能力水準。接下來,我們就從這個問題出發,探讨你應該掌握的基礎知識和答題思路,以及延伸的面試考點。

案例背景

以京東系統為例,使用者在購買商品時,通常會選擇用京豆抵扣一部分的金額,在這個過程中,交易服務和京豆服務通過 MQ 消息隊列進行通信。在下單時,交易服務發送“扣減賬戶 X 100 個京豆”的消息給 MQ 消息隊列,而京豆服務則在消費端消費這條指令,實作真正的扣減操作。

面試官:MQ 消息丢失、重複、積壓問題,如何解決?

那在這個過程中你會遇到什麼問題呢?

案例分析

要知道,在網際網路面試中,引入 MQ 消息中間件最直接的目的是:做系統解耦合流量控制,追其根源還是為了解決網際網路系統的高可用和高性能問題。

系統解耦:

用MQ消息隊列,可以隔離系統上下遊環境變化帶來的不穩定因素,比如京豆服務的系統需求無論如何變化,交易服務不用做任何改變,即使當京豆服務出現故障,主交易流程也可以将京豆服務降級,實作交易服務和京豆服務的解耦,做到了系統的高可用。

流量控制:

遇到秒殺等流量突增的場景,通過 MQ 還可以實作流量的“削峰填谷”的作用,可以根據下遊的處理能力自動調節流量。不過引入 MQ雖然實作了系統解耦合流量控制,也會帶來其他問題。

引入 MQ 消息中間件實作系統解耦,會影響系統之間資料傳輸的一緻性。

在分布式系統中,如果兩個節點之間存在資料同步,就會帶來資料一緻性的問題。同理,在這一講你要解決的就是:消息生産端和消息消費端的消息資料一緻性問題(也就是如何確定消息不丢失)。

而引入MQ 消息中間件解決流量控制, 會使消費端處理能力不足進而導緻消息積壓,這也是你要解決的問題。

是以你能發現,問題與問題之間往往是環環相扣的,面試官會借機考察你解決問題思路的連貫性和知識體系的掌握程度。

那面對“在使用 MQ 消息隊列時,如何確定消息不丢失”這個問題時,你要怎麼回答呢?首先,你要分析其中有幾個考點,比如:

如何知道有消息丢失?

哪些環節可能丢消息?

如何確定消息不丢失?

候選人在回答時,要先讓面試官知道你的分析思路,然後再提供解決方案:網絡中的資料傳輸不可靠,想要解決如何不丢消息的問題,首先要知道哪些環節可能丢消息,以及我們如何知道消息是否丢失了,最後才是解決方案(而不是上來就直接說自己的解決方案)。就好比“架構設計”“架構”展現了架構師的思考過程,而“設計”才是最後的解決方案,兩者缺一不可。

案例解答

我們首先來看消息丢失的環節,一條消息從生産到消費完成這個過程,可以劃分三個階段,分别為消息生産階段,消息存儲階段和消息消費階段。

面試官:MQ 消息丢失、重複、積壓問題,如何解決?

消息生産階段:

從消息被生産出來,然後送出給 MQ 的過程中,隻要能正常收到 MQ Broker 的 ack

确認響應,就表示發送成功,是以隻要處理好傳回值和異常,這個階段是不會出現消息丢失的。

消息存儲階段:

這個階段一般會直接交給 MQ 消息中間件來保證,但是你要了解它的原理,比如 Broker 會做副本,保證一條消息至少同步兩個節點再傳回ack。

消息消費階段:

消費端從 Broker 上拉取消息,隻要消費端在收到消息後,不立即發送消費确認給

Broker,而是等到執行完業務邏輯後,再發送消費确認,也能保證消息的不丢失。

方案看似萬無一失,每個階段都能保證消息的不丢失,但在分布式系統中,故障不可避免,作為消息生産端,你并不能保證 MQ 是不是弄丢了你的消息,消費者是否消費了你的消息,是以,本着 Design for Failure 的設計原則,你還是需要一種機制,來 Check 消息是否丢失了。

緊接着,你還可以向面試官闡述怎麼進行消息檢測?

總體方案解決思路為:在消息生産端,給每個發出的消息都指定一個全局唯一 ID,或者附加一個連續遞增的版本号,然後在消費端做對應的版本校驗。

具體怎麼落地實作呢?

你可以利用攔截器機制。在生産端發送消息之前,通過攔截器将消息版本号注入消息中(版本号可以采用連續遞增的 ID 生成,也可以通過分布式全局唯一 ID生成)。然後在消費端收到消息後,再通過攔截器檢測版本号的連續性或消費狀态,這樣實作的好處是消息檢測的代碼不會侵入到業務代碼中,可以通過單獨的任務來定位丢失的消息,做進一步的排查。

這裡需要你注意:

如果同時存在多個消息生産端和消息消費端,通過版本号遞增的方式就很難實作了,因為不能保證版本号的唯一性,此時隻能通過全局唯一 ID 的方案來進行消息檢測,具體的實作原理和版本号遞增的方式一緻。

現在,你已經知道了哪些環節(消息存儲階段、消息消費階段)可能會出問題,并有了如何檢測消息丢失的方案,然後就要給出解決防止消息丢失的設計方案。

回答完“如何確定消息不會丢失?” 之後,面試官通常會追問“怎麼解決消息被重複消費的問題?”

比如:在消息消費的過程中,如果出現失敗的情況,通過補償的機制發送方會執行重試,重試的過程就有可能産生重複的消息,那麼如何解決這個問題?

這個問題其實可以換一種說法,就是如何解決消費端幂等性問題(幂等性,就是一條指令,任意多次執行所産生的影響均與一次執行的影響相同),隻要消費端具備了幂等性,那麼重複消費消息的問題也就解決了。

我們還是來看扣減京豆的例子,将賬戶 X 的金豆個數扣減 100 個,在這個例子中,我們可以通過改造業務邏輯,讓它具備幂等性。

面試官:MQ 消息丢失、重複、積壓問題,如何解決?

最簡單的實作方案,就是在資料庫中建一張消息日志表,

這個表有兩個字段:消息 ID 和消息執行狀态。這樣,我們消費消息的邏輯可以變為:在消息日志表中增加一條消息記錄,然後再根據消息記錄,異步操作更新使用者京豆餘額。

因為我們每次都會在插入之前檢查是否消息已存在,是以就不會出現一條消息被執行多次的情況,這樣就實作了一個幂等的操作。當然,基于這個思路,不僅可以使用關系型資料庫,也可以通過 Redis 來代替資料庫實作唯一限制的方案。

在這裡我多說一句,想要解決“消息丢失”和“消息重複消費”的問題,有一個前提條件就是要實作一個全局唯一 ID 生成的技術方案。這也是面試官喜歡考察的問題,你也要掌握。

在分布式系統中,全局唯一 ID 生成的實作方法有資料庫自增主鍵、UUID、Redis,Twitter-Snowflake 算法,我總結了幾種方案的特點,你可以參考下。

面試官:MQ 消息丢失、重複、積壓問題,如何解決?

我提醒你注意,無論哪種方法,如果你想同時滿足簡單、高可用和高性能,就要有取舍,是以你要站在實際的業務中,說明你的選型所考慮的平衡點是什麼。我個人在業務中比較傾向于選擇 Snowflake 算法,在項目中也進行了一定的改造,主要是讓算法中的 ID 生成規則更加符合業務特點,以及優化諸如時鐘回撥等問題。

當然,除了“怎麼解決消息被重複消費的問題?”之外,面試官還會問到你“消息積壓”。

原因在于消息積壓反映的是性能問題,解決消息積壓問題,可以說明候選者有能力處理高并發場景下的消費能力問題。

你在解答這個問題時,依舊要傳遞給面試官一個這樣的思考過程:

如果出現積壓,那一定是性能問題,想要解決消息從生産到消費上的性能問題,就首先要知道哪些環節可能出現消息積壓,然後在考慮如何解決。

因為消息發送之後才會出現積壓的問題,是以和消息生産端沒有關系,又因為絕大部分的消息隊列單節點都能達到每秒鐘幾萬的處理能力,相對于業務邏輯來說,性能不會出現在中間件的消息存儲上面。毫無疑問,出問題的肯定是消息消費階段,

那麼從消費端入手,如何回答呢?

如果是線上突發問題,要臨時擴容,增加消費端的數量,與此同時,降級一些非核心的業務。通過擴容和降級承擔流量,這是為了表明你對應急問題的處理能力。

其次,才是排查解決異常問題,如通過監控,日志等手段分析是否消費端的業務邏輯代碼出現了問題,優化消費端的業務處理邏輯。

最後,如果是消費端的處理能力不足,可以通過水準擴容來提供消費端的并發處理能力

但這裡有一個考點需要特别注意,

那就是在擴容消費者的執行個體數的同時,必須同步擴容主題 Topic

的分區數量,確定消費者的執行個體數和分區數相等。如果消費者的執行個體數超過了分區數,由于分區是單線程消費,是以這樣的擴容就沒有效果。

比如在Kafka 中,一個 Topic 可以配置多個 Partition(分區),資料會被寫入到多個分區中,但在消費的時候,Kafka 約定一個分區隻能被一個消費者消費,Topic 的分區數量決定了消費的能力,是以,可以通過增加分區來提高消費者的處理能力。

總結 至此,我們講解了 MQ 消息隊列的熱門問題的解決方案,無論是國中級還是進階研發工程師,本篇文章的内容都是你需要掌握的,你都可以從這幾點出發,與面試官進行友好的交流。我來總結一下今天的重點内容。

如何確定消息不會丢失?

你要知道一條消息從發送到消費的每個階段,是否存在丢消息,以及如何監控消息是否丢失,最後才是如何解決問題,方案可以基于“ MQ 的可靠消息投遞

”的方式。

如何保證消息不被重複消費?

在進行消息補償的時候,一定會存在重複消息的情況,那麼如何實作消費端的幂等性就這道題的考點。

如何處理消息積壓問題?

這道題的考點就是如何通過 MQ

實作真正的高性能,回答的思路是,本着解決線上異常為最高優先級,然後通過監控和日志進行排查并優化業務邏輯,最後是擴容消費端和分片的數量。

在回答問題的時候,你需要特别注意的是,讓面試官了解到你的思維過程,這種解決問題的能力是面試官更為看中的,比你直接回答一道面試題更有價值。