更新時間:2021-09-15 10:44:56 來源:動力節(jié)點 瀏覽2063次
大家對RabbitMQ原理都應(yīng)該有所了解,下面我們就來看一下關(guān)于RabbitMQ原理的面試題。
其實就是問問你消息隊列都有哪些使用場景,然后你項目里具體是什么場景,說說你在這個場景里用消息隊列是什么?
先說一下消息隊列常見的使用場景吧,其實場景有很多,但是比較核心的有 3 個:解耦、異步、削峰。
解耦:多系統(tǒng)多進程的數(shù)據(jù)交換,用pub/sub
異步:把大數(shù)據(jù)量的同步處理改為異步
削峰:一般的A 系統(tǒng)使用 MySQL,扛到每秒 2k 個請求就差不多了,如果每秒請求到 5k 的話,可能就直接把 MySQL 給打死了,導(dǎo)致系統(tǒng)崩潰,用戶也就沒法再使用系統(tǒng)了。如果使用 MQ, 每秒 5k 個請求寫入 MQ,A 系統(tǒng)每秒鐘最多處理 2k 個請求,因為 MySQL 每秒鐘最多處理 2k 個。A 系統(tǒng)從 MQ 中慢慢拉取請求,每秒鐘就拉取 2k 個請求,不要超過自己每秒能處理的最 大請求數(shù)量就 ok,這樣下來,哪怕是高峰期的時候,A 系統(tǒng)也絕對不會掛掉,這又設(shè)計請求排隊的問題。
優(yōu)點:解耦、異步、削峰
缺點:
系統(tǒng)可用性降低
系統(tǒng)引入的外部依賴越多,越容易掛掉。
系統(tǒng)復(fù)雜度提高
一致性問題
A 系統(tǒng)處理完了直接返回成功了,人都以為你這個請求就成功了;但是問題是,要是 BCD 三個系統(tǒng)那里,BD 兩個系統(tǒng)寫庫成功了,結(jié)果 C 系統(tǒng)寫庫失敗了,這數(shù)據(jù)就不一致了。
RabbitMQ 有三種模式:單機模式、普通集群模式、鏡像集群模式
單機模式不存在高可用。
普通集群模式也不存在高可用性,意思就是在多臺機器上啟動多個 RabbitMQ 實例,每個機器啟動一個。但是你創(chuàng)建的 queue,只會放在一個 RabbitMQ 實例上,但是每個實例都同步 queue 的元數(shù)據(jù)(元數(shù)據(jù)可以認為是 queue 的一些配置信息,通過元數(shù)據(jù),可以找到 queue 所在實例)。你消費的時候,實際上如果連接到了另外一個實例,那么那個實例會從 queue 所在實例上 拉取數(shù)據(jù)過來。這種方式確實很麻煩,也不怎么好,沒做到所謂的分布式,就是個普通集群。因為這導(dǎo)致你要么消費者每次隨機連接一個實例然后拉取數(shù)據(jù),要么固定連接那個 queue 所在實 例消費數(shù)據(jù),前者有數(shù)據(jù)拉取的開銷,后者導(dǎo)致單實例性能瓶頸。而且如果那個放 queue 的實例宕機了,會導(dǎo)致接下來其他實例就無法從那個實例拉取,如果你開啟了消息持久化,讓 RabbitMQ 落地存儲消息的話,消息不一定會丟,得等這個實例恢復(fù)了,然后才可以繼續(xù)從這個 queue 拉取數(shù)據(jù)。
鏡像集群模式的策略是高可用策略,指定的時候可以要求數(shù)據(jù)同步到所有節(jié)點的,也可以要求同步到指定數(shù)量的節(jié)點,再次創(chuàng)建 queue 的時候,應(yīng)用這個策略,就會自動將數(shù)據(jù)同步到其他的 節(jié)點上去了。
其實本質(zhì)針對的場景,都是說,可能你的消費端出了問題,不消費了;或者消費的速度極其慢,造成消息堆積了,MQ存儲快要爆了,甚至開始過期失效刪除數(shù)據(jù)了。
針對這個問題可以有事前、事中、事后三種處理
事前:開發(fā)預(yù)警程序,監(jiān)控最大的可堆積消息數(shù),超過就發(fā)預(yù)警消息(比如短信),不要等出生產(chǎn)事故了再處理。
事中:看看消費端是不是故障停止了,緊急重啟。
事后:需要對消費端緊急擴容 ,增加處理消費者進程,如擴充10倍處理,但其實這也有個問題,即數(shù)據(jù)庫的吞吐是有限制的,如果是消費到數(shù)據(jù)庫也是沒辦法巨量擴容的,所以還是要在吞吐能力支持下老老實實的泄洪消 費。所以事前預(yù)防還是最重要的。否則出發(fā)刪除過期數(shù)據(jù),那就需要再重寫生產(chǎn)消息的程序,重新產(chǎn)生消息。
需要考慮3個可能丟數(shù)據(jù)的地方:生產(chǎn)端、隊列本身、消費端
(1)生產(chǎn)端:開啟事務(wù)(不推薦,太耗性能降低吞吐),推薦開啟 confirm 模式,在生產(chǎn)者那里設(shè)置開啟 confirm 模式之后,你每次寫的消息都會分配一個唯一的 id,然后如果寫入了RabbitMQ 中,RabbitMQ 會給你回傳一個 ack 消息,告訴你說這個消息 ok 了。如果 RabbitMQ 沒能處理這個消息,會回調(diào)你的一個 nack 接口,告訴你這個消息接收失敗,你可以重試。而 且你可以結(jié)合這個機制自己在內(nèi)存里維護每個消息 id 的狀態(tài),如果超過一定時間還沒接收到這個消息的回調(diào),那么你可以重發(fā)。
(2)隊列本身:就是 RabbitMQ 自己弄丟了數(shù)據(jù),這個你必須開啟 RabbitMQ 的持久化,就是消息寫入之后會持久化到磁盤,哪怕是 RabbitMQ 自己掛了,恢復(fù)之后會自動讀取之前存儲的數(shù)據(jù),一般數(shù)據(jù)不會丟。
設(shè)置持久化有兩個步驟:
創(chuàng)建 queue 的時候?qū)⑵湓O(shè)置為持久化,這樣就可以保證 RabbitMQ 持久化 queue 的元數(shù)據(jù),但是它是不會持久化 queue 里的數(shù)據(jù)的。
第二個是發(fā)送消息的時候?qū)⑾⒌?deliveryMode 設(shè)置為 2。就是將消息設(shè)置為持久化的,此時 RabbitMQ 就會將消息持久化到磁盤上去。
(3)消費端:其實和kafka的原理很類似,kafka即手動提交offsize。用RabbitMQ 提供的 ack 機制,簡單來說,就是你必須關(guān)閉 RabbitMQ 的自動 ack,通過自己的一個 api 來調(diào)用就行,然后每次你自己代碼里確保處理完的時候,再在程序里 ack。這樣的話,如果你還沒處理完,不就沒有 ack 了?那 RabbitMQ 就認為你還沒處理完,這個時候 RabbitMQ 會把這個消費分配給別 的 consumer 去處理,消息是不會丟的。
這個需要靈活作答,考察的是思考力,因為消費的場景有很多,有數(shù)據(jù)庫、有緩存、有第三方接口
(1)比如針對數(shù)據(jù)庫,你拿到這個消息做數(shù)據(jù)庫的insert操作。那就容易了,給這個消息做一個唯一主鍵(或者UUID),那么就算出現(xiàn)重復(fù)消費的情況,就會導(dǎo)致主鍵沖突,避免數(shù)據(jù)庫出現(xiàn)臟數(shù)據(jù)。
(2)再比如redis緩存,你拿到這個消息做redis的set的操作,那就容易了,不用解決,因為你無論set幾次結(jié)果都是一樣的,set操作本來就算冪等操作。
(3)再比如第三方接口,需要確定兩點,第三方接口程序是有去重能力的,那么臟一點直接丟數(shù)據(jù)過去,如果沒有去重能力,還是需要我們來寫程序去重,就是第2點的辦法。
節(jié)點的存儲類型分為兩種:
磁盤節(jié)點
內(nèi)存節(jié)點
磁盤節(jié)點就是配置信息和元信息存儲在磁盤上,內(nèi)存節(jié)點把這些信息存儲在內(nèi)存中,當(dāng)然內(nèi)次節(jié)點的性能是大大超越磁盤節(jié)點的。
單節(jié)點系統(tǒng)必須是磁盤節(jié)點,否則每次你重啟RabbitMQ之后所有的系統(tǒng)配置信息都會丟失。
RabbitMQ要求集群中至少有一個磁盤節(jié)點,當(dāng)節(jié)點加入和離開集群時,必須通知磁盤節(jié)點。
以上就是關(guān)于“RabbitMQ原理面試題”的介紹,你都答對了嗎?如果還想了解更多相關(guān)知識,可以關(guān)注一下動力節(jié)點RabbitMQ教程。教程內(nèi)容由淺到深,細致全面,當(dāng)然也有配套的視頻教程供可以免費下載學(xué)習(xí),希望對大家能夠有所幫助。