MQ問題梳理轉自陳琰AC

一逝她、為什么使用 MQ?

1.1 解耦

1.1.1 解耦

例如電商系統(tǒng)核心是交易服務,交易服務要調用另外三個服務睬捶,訂單服務黔宛、庫存服務、倉儲服務擒贸。


image.png

這三個服務如果有一個服務不可用臀晃,交易服務就無法正常運行,所以交易服務是強耦合另外三個服務介劫。

引入MQ之后徽惋,交易服務只跟MQ交互,把消息發(fā)到MQ里面就行了座韵,無需關心另外三個服務是否可用险绘。這時候交易服務跟另外三個服務就是弱耦合的關系踢京,耦合性被降低了。

哪怕是另外三個服務暫時不可用宦棺,也不影響交易服務的運行瓣距,只要其他服務運行起來后,把MQ里面的消息消費了就行代咸。(降級接口)


image.png
1.1.2 解耦

假設 A 系統(tǒng)在用戶發(fā)生某個操作的時候蹈丸,需要把用戶提交的數據同時推送到 B、C 兩個系統(tǒng)的時候呐芥。這個時候負責 A 系統(tǒng)的哥們想:沒事啊逻杖,B、C 兩個系統(tǒng)給我提供一個 HTTP 接口或者 RPC 接口思瘟,我把數據推送過去不就完事了嘛荸百,負責 A 系統(tǒng)的哥們美滋滋。

一切看起來很美好潮太,但是隨著業(yè)務快速迭代管搪,這個時候系統(tǒng) D 也想要這個數據。那既然這樣铡买,A 系統(tǒng)的開發(fā)同學就改咯,在發(fā)送數據給 B霎箍、C 的同時加上一個 D奇钞。但是,越到后面越發(fā)現漂坏,麻煩來了景埃。整個系統(tǒng)好像不止這個數據要發(fā)送給 B、C顶别、D谷徙、還有第二、第三個數據要發(fā)送給 B驯绎、C完慧、D。甚至有時候又加入了 E剩失、F 等系統(tǒng)屈尼,他們也要這個數據。并且有時候可能 B 系統(tǒng)突然又不要這個數據了拴孤,A 系統(tǒng)改來改去脾歧,A 系統(tǒng)的開發(fā)哥們頭皮發(fā)麻。更復雜的場景是演熟,數據通過接口傳給其他系統(tǒng)有時候還要考慮重試鞭执、超時等一些異常情況。

這個時候,就該我們的 MQ 粉墨登場了兄纺,這種情況下使用 MQ 來解耦是再合適不過了大溜,因為負責 A 系統(tǒng)的哥們只需要把消息扔到 MQ 就行了,其他系統(tǒng)按需來訂閱消息就好了囤热。就算某個系統(tǒng)不需要這個數據了猎提,也不會需要 A 系統(tǒng)改動代碼。

1.2 異步

沒有引入MQ的時候旁蔼,交易服務需要同步調用三個服務锨苏,如果調用一個服務需要耗時1秒,那么同步調用三個服務需要耗時3秒棺聊。在引入MQ之后伞租,全都改成了異步調用,整個耗時不到1秒限佩,大大提高了接口的性能葵诈。

1.3 削峰

如果一秒內同時來了5000筆交易,而訂單服務每秒只能處理100筆交易祟同,那么后面的4900筆交易失敗作喘。在引入MQ之后,交易服務可以把交易數據先發(fā)送到MQ中晕城,而訂單服務再慢慢從MQ拉取交易信息處理泞坦。從而避免突發(fā)流量壓垮服務器。

1.3.1 削峰填谷

舉個例子砖顷,比如我們的訂單系統(tǒng)贰锁,在下單的時候就會往數據庫寫數據。但是數據庫只能支撐每秒 1000 左右的并發(fā)寫入滤蝠,并發(fā)量再高就容易宕機豌熄。低峰期的時候并發(fā)也就 100 多個,但是在高峰期時候物咳,并發(fā)量會突然激增到 5000 以上锣险,這個時候數據庫肯定死了。

但是使用了 MQ 之后所森,情況就變了囱持,消息被 MQ 保存起來了,然后系統(tǒng)就可以按照自己的消費能力來消費焕济,比如每秒 1000 個數據纷妆,這樣慢慢寫入數據庫,這樣就不會打死數據庫了晴弃。

至于為什么叫做削峰填谷呢掩幢?如果沒有用 MQ 的情況下逊拍,并發(fā)量高峰期的時候是有一個“頂峰”的,然后高峰期過后又是一個低并發(fā)的“谷”际邻。但是使用了 MQ 之后芯丧,限制消費消息的速度為 1000QPS,但是這樣一來世曾,高峰期產生的數據勢必會被積壓在 MQ 中缨恒,高峰就被“削”掉了。但是因為消息積壓轮听,在高峰期過后的一段時間內骗露,消費消息的速度還是會維持在 1000QPS,直到消費完積壓的消息血巍,這就叫做“填谷”萧锉。

二、引入MQ之后的問題

2.1 系統(tǒng)可用性降低

本來整個系統(tǒng)有四個服務述寡,我們只需要保證這四個服務可用就行了∈料叮現在又多引入了一個MQ,我們還要保證MQ的可用鲫凶,所以整個系統(tǒng)的可用性降低禀崖。

2.2 系統(tǒng)復雜性提高

本來交易服務是同步調用另外三個服務,如果另外三個服務不可用螟炫,交易服務能立即感知到帆焕。引入MQ之后,整個系統(tǒng)的穩(wěn)定性就要靠MQ保證了不恭。

這時候,我們就要考慮到發(fā)到MQ里面的消息怎么避免丟失的問題财饥?順序性消費的問題换吧,就是同一筆交易的下單消息應該比撤單消息先處理。重復性消費的問題钥星,就是同一筆下單交易的消息可能被多次處理沾瓦。

當然,每種問題都有具體的解決方案谦炒,避免消息丟失可以使用MQ集群贯莺,順序性消費可以把消息發(fā)到同一個分區(qū),重復性消費可以在消費端做冪等性處理宁改。

2.3 重復消費問題

2.3.1 問題場景

重復消費問題可以說是 MQ 中普遍存在的問題缕探, 不管你用哪種 MQ 都無法避免。有哪些場景會出現重復的消息呢还蹲?

  • 消息生產者產生了重復的消息爹耗;
  • Kafka 和 RocketMQ 的 offset 被回調了耙考;
  • 消息消費者確認失敗潭兽;
  • 消息消費者確認時超時倦始;
  • 業(yè)務系統(tǒng)主動發(fā)起重試。

如果重復消息不做正確的處理山卦,會對業(yè)務造成很大的影響鞋邑,產生重復數據或者導致數據異常,比如會員系統(tǒng)多開通了一個月的會員等账蓉。

2.3.2 解決方案

不管是由于生產者產生的重復消息枚碗,還是由于消費者導致的重復消息,我們都可以在消費者中解決這個問題剔猿。

這就要求消費者在做業(yè)務處理時视译,要做冪等設計。在這里我推薦增加一張消費消息表归敬,來解決 MQ的這類問題酷含。

消費消息表中,使用 messageId 做唯一索引汪茧。在處理業(yè)務邏輯之前椅亚,先根據 messageId 查詢一下該消息有沒有處理過。如果已經處理過了則直接返回成功舱污,如果沒有處理過呀舔,則繼續(xù)做業(yè)務處理。


image.png

2.4 數據一致性問題(異步分布式事務問題)

2.4.1 問題場景

當服務間是同步調用的時候扩灯,我們還可以使用本地事務來控制數據的一致性媚赖。但是引入MQ之后,服務間的調用都是異步了珠插,就沒辦法使用本地事務惧磺,也就無法做到數據的強一致性了。

例如捻撑,調用訂單服務下單成功了磨隘,但是調用庫存服務扣減庫存失敗,就會導致超賣顾患,是嚴重的線上事故番捂。

這時候怎么辦?
方案一:需要事務強一致的江解,不用消息異步设预,如下單、減庫存要放在一個事務里控制膘流,加積分這種非核心的業(yè)務才用消息異步處理絮缅。

image.png

方案二:可以使用MQ事務消息(只有RocketMQ才有事務消息功能鲁沥,RocketMQ收發(fā)事務消息)。

事務狀態(tài)有以下三種:

  • TransactionStatus.CommitTransaction:提交事務耕魄,允許訂閱方消費該消息画恰。
    TransactionStatus.RollbackTransaction:回滾事務,消息將被丟棄不允許消費吸奴。
    TransactionStatus.Unknow:無法判斷狀態(tài)允扇,期待消息隊列RocketMQ版的Broker向發(fā)送方再次詢問該消息對應的本地事務的狀態(tài)。
步驟一: A 服務向消息中間件發(fā)布消息
  • 在服務A處理任務A前则奥,首先向消息中間件發(fā)送一條半信息考润。
  • 消息中間件收到后將該消息持久化,但不進行投遞读处。持久化成功后糊治,向A服務返回確認應答。
  • 服務A收到確認應答后罚舱,便可以開始處理任務A井辜。
  • 任務A處理完成后,服務A便會向消息中間件發(fā)送Commit 或者 Rollback 請求管闷,該請求發(fā)送完成后粥脚,服務A的工作任務就結束了,該事務的處理過程也就結束了包个。
  • 在消息中間件收到 Commit 后刷允,便會向 B 服務投遞消息,如果收到 Rollback 便會直接丟棄消息碧囊。

如果消息中間件在最后的過程中树灶,長時間沒有收到服務A 發(fā)送的 Commit 或 Rollback 指令,這個時候就需要依靠 超時詢問機制糯而。

步驟二: 消息中間件向B服務投遞消息

消息中間件收到A服務的提交 Commit指令后便會將該消息投遞給B服務破托,然后將自己的狀態(tài)置為阻塞等待狀態(tài)。B服務收到消息中間件發(fā)送的消息后便開始處理任務B歧蒋,處理完成后便會向消息中間件發(fā)出回應。但是在消息中間件阻塞等待的時候同樣會出現問題州既。

正常情況:消息中間件投遞完消息后谜洽,進入阻塞等待狀態(tài),在收到確認應答后便認為事務處理完成吴叶,該流程結束阐虚。
等待超時情況:在等待確認應答超時之后就會重新進行投遞,直到B服務器返回消費成功響應為止蚌卤。而消息重試的次數和時間間隔都可以設置实束,如果最終還是不能成功進行投遞奥秆,則需要人工干預。

2.4.2 解決方案

我們都知道數據一致性分為:強一致性咸灿、弱一致性构订、最終一致性。

而 MQ 為了性能考慮使用的是最終一致性避矢,那么必定會出現數據不一致的問題悼瘾。這類問題大概率是因為消費者讀取消息后,業(yè)務邏輯處理失敗導致的审胸。這時候可以增加重試機制亥宿。重試分為同步重試和異步重試。

有些消息量比較小的業(yè)務場景砂沛,可以采用同步重試烫扼。在消費消息時如果處理失敗,立刻重試 3-5 次碍庵,如果還是失敗則寫入到記錄表中映企。但如果消息量比較大,則不建議使用這種方式怎抛。因為如果出現網絡異常卑吭,可能會導致大量的消息不斷重試,影響消息讀取速度造成消息堆積马绝。


image.png

消息量比較大的業(yè)務場景豆赏,建議采用異步重試。在消費者處理失敗之后富稻,立刻寫入重試表掷邦,有個 job(如采用xxljob) 專門定時重試。

還有一種做法:如果消費失敗椭赋,自己給同一個 topic 發(fā)一條消息抚岗。在后面的某個時間點,自己又會消費到那條消息哪怔,起到了重試的效果宣蔚。如果對消息順序要求不高的場景,可以使用這種方式认境。

2.5 消息丟失問題

2.5.1 問題場景

同樣消息丟失問題胚委,也是 MQ 中普遍存在的問題,不管你用哪種 MQ 都無法避免叉信。有哪些場景會出現消息丟失問題呢亩冬?

  • 生產者產生消息時,由于網絡原因發(fā)送到 MQ 失敗了硼身;
  • MQ 服務器持久化硅急,存儲磁盤時出現異常覆享;
  • Kafka和RocketMQ 的 offset 被回調時,略過了很多消息营袜;
  • 消費者剛讀取消息撒顿,已經 ACK 確認,但業(yè)務還沒處理完连茧,服務就被重啟了核蘸。

導致消息丟失問題的原因挺多的, 生產者啸驯、 MQ 服務器客扎、 消費者都有可能產生問題。我在這里就不一一列舉了罚斗。最終的結果會導致消費者無法正確的處理消息徙鱼,而導致數據不一致的情況。

2.5.2 解決方案

不管你是否承認针姿,有時候消息真的會丟袱吆。即使這種概率非常小,也會對業(yè)務有影響距淫。生產者绞绒、MQ 服務器、消費者都有可能會導致消息丟失的問題榕暇。為了解決這個問題蓬衡,我們可以增加一張消息發(fā)送表。

  • 當生產者發(fā)完消息之后彤枢,會往該表中寫入一條數據狰晚,狀態(tài) status 標記為待確認;

  • 如果消費者讀取消息之后缴啡,調用生產者的 API 更新該消息的status為已確認壁晒;

  • 有個job(xxljob) 每隔一段時間檢查一次消息發(fā)送表,如果5分鐘(這個時間可以根據實際情況來定)后還有狀態(tài)是待確認的消息业栅,則認為該消息已經丟失了秒咐,重新發(fā)條消息。


    image.png

    這樣不管是由于生產者碘裕、 MQ服務器反镇、還是消費者導致的消息丟失問題,job 都會重新發(fā)消息娘汞。

2.6 消息順序問題

2.6.1 問題場景

有些業(yè)務數據是有狀態(tài)的,比如訂單有下單夕玩、支付你弦、完成惊豺、退貨等狀態(tài)。 如果訂單數據作為消息體禽作,就會涉及順序問題了尸昧。

例如消費者收到同一個訂單的兩條消息。第一條消息的狀態(tài)是下單旷偿,第二條消息的狀態(tài)是支付烹俗,這是沒問題的。但如果第一條消息的狀態(tài)是支付萍程,第二條消息的狀態(tài)是下單就會有問題了幢妄。沒有下單就先支付了?


image.png

消息順序問題是一個非常棘手的問題茫负,比如:

  • Kafka 同一個 partition 中能保證順序蕉鸳,但是不同的 partition 無法保證順序;

  • RabbitMQ的同一個queue能夠保證順序忍法,但是如果多個消費者同一個queue 也會有順序問題潮尝。

  • 如果消費者使用多線程消費消息,也無法保證順序饿序。

  • 如果消費消息時同一個訂單的多條消息中勉失,中間的一條消息出現異常情況,順序將會被打亂原探。

  • 還有如果生產者發(fā)送到 MQ中的路由規(guī)則乱凿,跟消費者不一樣,也無法保證順序踢匣。

2.6.2 解決方案

消息順序問題是一種常見問題告匠。我們以 Kafka 消費訂單消息為例,訂單有下單离唬、 支付后专、 完成、 退貨等狀態(tài)输莺。這些狀態(tài)是有先后順序的戚哎,如果順序錯了會導致業(yè)務異常。

解決這類問題之前嫂用,我們需要先確認:消費者是否真的需要知道中間狀態(tài)型凳,只知道最終狀態(tài)行不行?


image.png

其實很多時候嘱函,我真的需要知道的是最終狀態(tài)甘畅。這時可以把流程優(yōu)化一下:


image.png

這種方式可以解決大部分的消息順序問題。

但如果真的有需要保證消息順序的需求,那么可以將訂單號路由到不同的 partition疏唾。同一個訂單號的消息蓄氧,每次到發(fā)到同一個partition。
2.7 消息堆積
2.7.1 問題場景
如果消息消費者讀取消息的速度槐脏,能夠跟上消息生產者的節(jié)奏喉童,那么整套 MQ 機制就能發(fā)揮最大作用。

但是很多時候顿天,由于某些批處理或者其他原因堂氯,導致消費速度小于生產速度。這樣會直接導致消息堆積問題牌废,從而影響業(yè)務功能咽白。

這里以下單 開通會員為例,如果消息出現堆積會導致用戶下單之后畔规,很久之后才能變成會員局扶。這種情況肯定會引起大量用戶投訴。

2.7.2 解決方案
那么消息堆積問題該如何解決呢叁扫?這個要看消息是否需要保證順序三妈。如果不需要保證順序,可以讀取消息之后用多線程處理業(yè)務邏輯莫绣。


image.png

這樣就能增加業(yè)務邏輯處理速度畴蒲,解決消息堆積問題。但是線程池的核心線程數和最大線程數需要合理配置对室,不然可能會浪費系統(tǒng)資源模燥。

如果需要保證順序,可以讀取消息之后將消息按照一定的規(guī)則分發(fā)到多個隊列中掩宜,然后在隊列中用單線程處理蔫骂。

image.png

資料來源:
面試官竟然問我為啥要用MQ,幸虧我看了參考答案

面霸篇:MQ 的 5 大問題詳解

消息中間件學習總結(18)——MQ常見面試題總結

作者:陳琰AC
鏈接:http://www.reibang.com/p/439f74dd62a9

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末牺汤,一起剝皮案震驚了整個濱河市辽旋,隨后出現的幾起案子,更是在濱河造成了極大的恐慌檐迟,老刑警劉巖补胚,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異追迟,居然都是意外死亡溶其,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門敦间,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瓶逃,“玉大人束铭,你說我怎么就攤上這事∠峋” “怎么了纯露?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長代芜。 經常有香客問我,道長浓利,這世上最難降的妖魔是什么挤庇? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮贷掖,結果婚禮上嫡秕,老公的妹妹穿的比我還像新娘。我一直安慰自己苹威,他們只是感情好昆咽,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著牙甫,像睡著了一般掷酗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上窟哺,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天泻轰,我揣著相機與錄音,去河邊找鬼且轨。 笑死浮声,一個胖子當著我的面吹牛,可吹牛的內容都是我干的旋奢。 我是一名探鬼主播泳挥,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼至朗!你這毒婦竟也來了屉符?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤爽丹,失蹤者是張志新(化名)和其女友劉穎筑煮,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體粤蝎,經...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡真仲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了初澎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秸应。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡虑凛,死狀恐怖,靈堂內的尸體忽然破棺而出软啼,到底是詐尸還是另有隱情桑谍,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布祸挪,位于F島的核電站锣披,受9級特大地震影響,放射性物質發(fā)生泄漏贿条。R本人自食惡果不足惜雹仿,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望整以。 院中可真熱鬧胧辽,春花似錦、人聲如沸公黑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凡蚜。三九已至人断,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間番刊,已是汗流浹背含鳞。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留芹务,地道東北人蝉绷。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像枣抱,于是被迫代替她去往敵國和親熔吗。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內容