1.你們為什么使用 mq?具體的使用場景是什么?
mq的作用很簡單,削峰填谷,以電商交易下單的場景來說,正向交易的程序可能涉及到創建訂單、扣減庫存、扣級訓動預算、扣級訓分等等,每個介面的耗時如果是100ms,那么理論上整個下單的鏈路就需要耗費400ms,這個時間顯然是太長了,

如果這些操作全部同步處理的話,首先呼叫鏈路太長影響介面性能,其次分布式事務的問題很難處理,這時候像扣減預算和積分這種對實時一致性要求沒有那么高的請求,完全就可以通過mq異步的方式去處理了,同時,考慮到異步帶來的不一致的問題,我們可以通過job去重試保證介面呼叫成功,而且一般公司都會有核對的平臺,比如下單成功但是未扣級訓分的這種問題可以通過核對作為兜底的處理方案,

使用mq之后我們的鏈路變簡單了,同時異步發送訊息我們的整個系統的抗壓能力也上升了,
2.那你們使用什么 mq?基于什么做的選型?
我們主要調研了幾個主流的mq,kafka、rabbitmq、rocketmq、activemq,選型我們主要基于以下幾個點去考慮:
1.由于我們系統的qps壓力比較大,所以性能是首要考慮的要素,
2.開發語言,由于我們的開發語言是java,主要是為了方便二次開發,
3.對于高并發的業務場景是必須的,所以需要支持分布式架構的設計,
4.功能全面,由于不同的業務場景,可能會用到順序訊息、事務訊息等,
基于以上幾個考慮,我們最終選擇了RocketMQ,

3.你上面提到異步發送,那訊息可靠性怎么保證?
訊息丟失可能發生在生產者發送訊息、MQ本身丟失訊息、消費者丟失訊息3個方面,
生產者丟失
生產者丟失訊息的可能點在于程式發送失敗拋例外了沒有重試處理,或者發送的程序成功但是程序中網路閃斷MQ沒收到,訊息就丟失了,
由于同步發送的一般不會出現這樣使用方式,所以我們就不考慮同步發送的問題,我們基于異步發送的場景來說,
異步發送分為兩個方式:異步有回呼和異步無回呼,無回呼的方式,生產者發送完后不管結果可能就會造成訊息丟失,而通過異步發送+回呼通知+本地訊息表的形式我們就可以做出一個解決方案,以下單的場景舉例,
1.下單后先保存本地資料和MQ訊息表,這時候訊息的狀態是發送中,如果本地事務失敗,那么下單失敗,事務回滾,
2.下單成功,直接回傳客戶端成功,異步發送MQ訊息
3.MQ回呼通知訊息發送結果,對應更新資料庫MQ發送狀態
4.JOB輪詢超過一定時間(時間根據業務配置)還未發送成功的訊息去重試
5.在監控平臺配置或者JOB程式處理超過一定次數一直發送不成功的訊息,告警,人工介入,

一般而言,對于大部分場景來說異步回呼的形式就可以了,只有那種需要完全保證不能丟失訊息的場景我們做一套完整的解決方案,
MQ 丟失
如果生產者保證訊息發送到MQ,而MQ收到訊息后還在記憶體中,這時候宕機了又沒來得及同步給從節點,就有可能導致訊息丟失,
比如RocketMQ:
RocketMQ分為同步刷盤和異步刷盤兩種方式,默認的是異步刷盤,就有可能導致訊息還未刷到硬碟上就丟失了,可以通過設定為同步刷盤的方式來保證訊息可靠性,這樣即使MQ掛了,恢復的時候也可以從磁盤中去恢復訊息,
比如Kafka也可以通過配置做到:
acks=all 只有參與復制的所有節點全部收到訊息,才回傳生產者成功,這樣的話除非所有的節點都掛了,訊息才會丟失, replication.factor=N,設定大于1的數,這會要求每個partion至少有2個副本 min.insync.replicas=N,設定大于1的數,這會要求leader至少感知到一個follower還保持著連接 retries=N,設定一個非常大的值,讓生產者發送失敗一直重試
雖然我們可以通過配置的方式來達到MQ本身高可用的目的,但是都對性能有損耗,怎樣配置需要根據業務做出權衡,
消費者丟失
消費者丟失訊息的場景:消費者剛收到訊息,此時服務器宕機,MQ認為消費者已經消費,不會重復發送訊息,訊息丟失,
RocketMQ默認是需要消費者回復ack確認,而kafka需要手動開啟配置關閉自動offset,
消費方不回傳ack確認,重發的機制根據MQ型別的不同發送時間間隔、次數都不盡相同,如果重試超過次數之后會進入死信佇列,需要手工來處理了,(Kafka沒有這些)

4.你說到消費者消費失敗的問題,那么如果一直消費失敗導致訊息積壓怎么處理?
因為考慮到時消費者消費一直出錯的問題,那么我們可以從以下幾個角度來考慮:
1.消費者出錯,肯定是程式或者其他問題導致的,如果容易修復,先把問題修復,讓consumer恢復正常消費
2.如果時間來不及處理很麻煩,做轉發處理,寫一個臨時的consumer消費方案,先把訊息消費,然后再轉發到一個新的topic和MQ資源,這個新的topic的機器資源單獨申請,要能承載住當前積壓的訊息
3.處理完積壓資料后,修復consumer,去消費新的MQ和現有的MQ資料,新MQ消費完成后恢復原狀

5.那如果訊息積壓達到磁盤上限,訊息被洗掉了怎么辦?
這,,,他媽都洗掉了我有啥辦法啊,,,冷靜,再想想,,有了,

最初,我們發送的訊息記錄是落庫保存了的,而轉發發送的資料也保存了,那么我們就可以通過這部分資料來找到丟失的那部分資料,再單獨跑個腳本重發就可以了,如果轉發的程式沒有落庫,那就和消費方的記錄去做對比,只是程序會更艱難一點,
6.說了這么多,那你說說 RocketMQ 實作原理吧?
RocketMQ由NameServer注冊中心集群、Producer生產者集群、Consumer消費者集群和若干Broker(RocketMQ行程)組成,它的架構原理是這樣的:
1.Broker在啟動的時候去向所有的NameServer注冊,并保持長連接,每30s發送一次心跳
2.Producer在發送訊息的時候從NameServer獲取Broker服務器地址,根據負載均衡演算法選擇一臺服務器來發送訊息
3.Conusmer消費訊息的時候同樣從NameServer獲取Broker地址,然后主動拉取訊息來消費

7.為什么 RocketMQ 不使用 Zookeeper 作為注冊中心呢?
我認為有以下幾個點是不使用zookeeper的原因:
1.根據CAP理論,同時最多只能滿足兩個點,而zookeeper滿足的是CP,也就是說zookeeper并不能保證服務的可用性,zookeeper在進行選舉的時候,整個選舉的時間太長,期間整個集群都處于不可用的狀態,而這對于一個注冊中心來說肯定是不能接受的,作為服務發現來說就應該是為可用性而設計,
2.基于性能的考慮,NameServer本身的實作非常輕量,而且可以通過增加機器的方式水平擴展,增加集群的抗壓能力,而zookeeper的寫是不可擴展的,而zookeeper要解決這個問題只能通過劃分領域,劃分多個zookeeper集群來解決,首先操作起來太復雜,其次這樣還是又違反了CAP中的A的設計,導致服務之間是不連通的,
3.持久化的機制來帶的問題,ZooKeeper 的 ZAB 協議對每一個寫請求,會在每個 ZooKeeper 節點上保持寫一個事務日志,同時再加上定期的將記憶體資料鏡像(Snapshot)到磁盤來保證資料的一致性和持久性,而對于一個簡單的服務發現的場景來說,這其實沒有太大的必要,這個實作方案太重了,而且本身存盤的資料應該是高度定制化的,
4.訊息發送應該弱依賴注冊中心,而RocketMQ的設計理念也正是基于此,生產者在第一次發送訊息的時候從NameServer獲取到Broker地址后快取到本地,如果NameServer整個集群不可用,短時間內對于生產者和消費者并不會產生太大影響,
8.那 Broker 是怎么保存資料的呢?
RocketMQ主要的存盤檔案包括commitlog檔案、consumequeue檔案、indexfile檔案,
Broker在收到訊息之后,會把訊息保存到commitlog的檔案當中,而同時在分布式的存盤當中,每個broker都會保存一部分topic的資料,同時,每個topic對應的messagequeue下都會生成consumequeue檔案用于保存commitlog的物理位置偏移量offset,indexfile中會保存key和offset的對應關系,

CommitLog檔案保存于${Rocket_Home}/store/commitlog目錄中,從圖中我們可以明顯看出來檔案名的偏移量,每個檔案默認1G,寫滿后自動生成一個新的檔案,

由于同一個topic的訊息并不是連續的存盤在commitlog中,消費者如果直接從commitlog獲取訊息效率非常低,所以通過consumequeue保存commitlog中訊息的偏移量的物理地址,這樣消費者在消費的時候先從consumequeue中根據偏移量定位到具體的commitlog物理檔案,然后根據一定的規則(offset和檔案大小取模)在commitlog中快速定位,

9.Master 和 Slave 之間是怎么同步資料的呢?
而訊息在master和slave之間的同步是根據raft協議來進行的:
1.在broker收到訊息后,會被標記為uncommitted狀態
2.然后會把訊息發送給所有的slave
3.slave在收到訊息之后回傳ack回應給master
4.master在收到超過半數的ack之后,把訊息標記為committed
5.發送committed訊息給所有slave,slave也修改狀態為committed
10.你知道 RocketMQ 為什么速度快嗎?
是因為使用了順序存盤、Page Cache和異步刷盤,
1.我們在寫入commitlog的時候是順序寫入的,這樣比隨機寫入的性能就會提高很多
2.寫入commitlog的時候并不是直接寫入磁盤,而是先寫入作業系統的PageCache
3.最后由作業系統異步將快取中的資料刷到磁盤
11.什么是事務、半事務訊息?怎么實作的?
事務訊息就是MQ提供的類似XA的分布式事務能力,通過事務訊息可以達到分布式事務的最終一致性,
半事務訊息就是MQ收到了生產者的訊息,但是沒有收到二次確認,不能投遞的訊息,
實作原理如下:
1.生產者先發送一條半事務訊息到MQ
2.MQ收到訊息后回傳ack確認
3.生產者開始執行本地事務
4.如果事務執行成功發送commit到MQ,失敗發送rollback
5.如果MQ長時間未收到生產者的二次確認commit或者rollback,MQ對生產者發起訊息回查
6.生產者查詢事務執行最終狀態
7.根據查詢事務狀態再次提交二次確認
最終,如果MQ收到二次確認commit,就可以把訊息投遞給消費者,反之如果是rollback,訊息會保存下來并且在3天后被洗掉,

作者 | 科技繆繆
本文來自博客園,作者:古道輕風,轉載請注明原文鏈接:https://www.cnblogs.com/88223100/p/MessqgeQueue-Interview-Questions.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/538539.html
標籤:其他
上一篇:資訊收集
下一篇:低代碼相關概念及釘釘宜搭初使用
