簡單的整理RabbitMQ的相關的知識蛉签,我們初步了解了RabbitMQ的相關知識胡陪,那么我們怎么在我們的工作中使用RabbitMQ呢,使用RabbitMQ應該需要注意哪些問題碍舍。我們以面試題的方法來展示柠座,不過本次的題目不包括集群和高可用方便的,這個等我梳理集群和高可用的相關知識后再整理對應的面試題
本博文參考網上很多關于RabbitMQ的經典面試題片橡,整理歸納并加上自己的理解妈经,意在梳理自己對RabbitMQ的理解以及更好地幫助廣大的碼友們,如有錯誤锻全,敬請指正
1. RabbitMQ的實用優(yōu)點
總得來說,有三大確實的好處
- 應用解耦
舉例說明:有這樣一個業(yè)務場景:我們在網上購買一件商品录煤,支付成功之后庫存要減少一鳄厌,如果是傳統(tǒng)的軟件架構中,必須是先支付妈踊,然后再減少庫存了嚎,這兩個操作必須是在同一事務中,即操作原子性廊营,但是這樣做的話效率是極其低下的歪泳,如果使用RabbitMQ的話,我們需要將消息發(fā)送給各自的隊列來進行消息處理露筒,支付和庫存的操作之間沒有了關聯性呐伞,這樣支付系統(tǒng)和庫存系統(tǒng)之間就進行了解耦。
- 流量削峰
rabbitMQ可以使用緩沖隊列的方式慎式,在訪問量急劇增大的時候伶氢,減少并發(fā)訪問的壓力趟径,比較常見的業(yè)務場景就是秒殺和簽到系統(tǒng),一般來說流量的削峰有兩個處理方式:
- 上游隊列緩沖癣防,限速發(fā)送
- 下游隊列緩沖蜗巧,限速執(zhí)行
當然,常見的場景是采用第二種方式蕾盯,不影響客戶使用的響應速度和使用體驗等幕屹,
我們知道RabbitMQ中消息是通過信道Channel來傳給對應的隊列的,而消費端監(jiān)聽這個隊列處理其中的消息也是有處理時間的级遭,這時我們需要解決的就是如果隊列上有一定數量的消息未被確認望拖,則不進行新的消息的消費
rabbitMQ提供channel.basicQos方法來限制信道上的消費者所能保持的最大未確認消息的數量,說到未確認装畅,我們需要先知道RabbitMQ為了保證消息可靠的到達消費者那里靠娱, 提供了消息確認機制,通過autoAck參數來控制掠兄,如果autoAck為true像云,默認消息消費者自動確認消息,此時消息可能未被處理結束蚂夕,如果autoAck為false的話迅诬,則需要消費者手動來確認消息
結合上面兩點,我們可以利用rabbitMQ在服務的下游來限速執(zhí)行達到流量削峰的目的
- 異步處理
很多的業(yè)務場景中婿牍,需要發(fā)出一個指示侈贷,但是并不要求立即執(zhí)行,可能對什么時候執(zhí)行等脂,或者只要執(zhí)行就可以了有不同的需求俏蛮,而對象這樣的RabbitMQ提供不同的解決方法,用戶發(fā)送發(fā)送的消息儲存在RabbitMQ中上遥,由rabbitMQ傳遞給消費者來進行消費搏屑,也可以通過死信隊列來實現延遲隊列的效果,讓消息定時被消費等等粉楚。
當然RabbitMQ還有很多其他的好處辣恋,比如:很容易實現集群環(huán)境的搭建,能定制路由設置消息傳遞的規(guī)則以及消息分發(fā)和消息緩沖等優(yōu)點
2. 消息基于什么傳輸模软,這樣做有什么優(yōu)點伟骨?
RabbitMQ是基于信道Channel的方式來傳輸數據,排除了使用TCP鏈接來進行數據的傳輸燃异,因為TCP鏈接創(chuàng)建和銷毀對于系統(tǒng)性能的開銷比較大携狭,且并發(fā)能力受系統(tǒng)資源的限制,這樣很容易造成rabbitMQ的性能瓶頸回俐。
消費者鏈接RabbitMQ其實就是一個TCP鏈接暑中,一旦鏈接創(chuàng)建成功之后壹瘟,就會基于鏈接創(chuàng)建Channel,每個線程把持一個Channel,Channel復用TCP鏈接鳄逾,減少了系統(tǒng)創(chuàng)建和銷毀鏈接的消耗稻轨,提高了性能
3. 如何確保消息正確的發(fā)送到RabbitMQ
發(fā)送方發(fā)送消息到RabbitMQ,有可能發(fā)送失敗雕凹,失敗的原因有如下的可能:
- 交換器無法根據自身的類型和路由鍵匹配到隊列(mandatory)
- 當與路由鍵匹配的所有隊列都沒有消費者時(延時隊列和死信隊列)
對于上面的情況殴俱,RabbitMQ提供發(fā)送方確認機制來去報消息正確發(fā)送到RabbitMQ服務
發(fā)送方確認機制是指消息生產者將信道設置成confirm模式,一旦信道進入confirm模式枚抵,所有在該信道上發(fā)布的消息都會指派一個唯一的id线欲,一旦消息被投遞到RabbitMQ服務中國,RabbitMQ就會發(fā)送一個確認給生產者汽摹。
發(fā)送方確認模式是異步的李丰,不影響生成者繼續(xù)發(fā)送消息,可擴展性也就增大了
其實還有另外一種解決方法逼泣,發(fā)送方如果覺得異步確認對于代碼而言變的復雜了趴泌,那么可以不設置mandatory
額外補充下,發(fā)送方確認其實還有事務機制拉庶,主要有如下的方法進行設置:
- channel.txSelect: 將當前的Channel設置為事務模式
- channel.txCommit: 提交當前的事務
- channel.txRollback: 事務回滾
跟數據庫的事務比較像嗜憔,也的確能解決發(fā)送方能確認消息是否發(fā)送到RabbitMQ中,但是事務機制很耗費性能氏仗,所以不提倡使用事務吉捶,僅供了解即可
4. 如何確保消息接收方消費消息
消費方通過監(jiān)聽隊列,從Channel中獲取隊列中存儲的數據并進行消費——即為消費者訂閱隊列皆尔,可以執(zhí)行autoAck參數呐舔,當autoAck為false時,RabbitMQ會等待消費者顯示的回復確認信號之后慷蠕,才會從內存(或者硬盤)中刪除消息珊拼。
所以確保消費者消費消息,只需要設置autoAck參數為false即可砌们,這樣就確保RabbitMQ會等待消息消費完成之后才刪除消息杆麸。但是這個特性卻衍生出新的問題搁进,如果消費端處理消息失敗浪感,沒有手動顯示回復確認信號, 則RabbitMQ不會在內存或硬盤中刪除該消息饼问,導致該消息會阻塞在隊列中影兽,后續(xù)的消息也會被阻塞住導致消息無法消費。
對于上面出現的問題莱革,我們可以使用RabbitMQ提供的補償機制和死信隊列來實現消費失敗的消息保存峻堰。
rabbitMQ提供配置參數來開啟消費者重試機制讹开,也能配置配置最大重試次數和重試間隔時間,rabbitMQ對于消息消費失敗達到一定次數后捐名,就會放棄該消息旦万,我們可以手動實現,如果消費失敗達到最大重試次數后镶蹋,將數據轉發(fā)到死信隊列上成艘,由死信隊列的消費者來實現消息的持久化到數據庫或者日志文件中,一般重試次數我們設置為3此贺归,間隔時間為5s淆两。
也可以消費者手動實現業(yè)務層判斷,如果同一消息消費了多次拂酣,則可以手動拒絕該消息秋冰,然后該消息自動進入死信隊列,這樣基于rabbitMQ的死信隊列的特性來自動實現消息的保存婶熬,優(yōu)點是rabbitMQ的封裝框架不需要進行額外的代碼剑勾,性能方面有了保障,缺點是這個工作交給了消費者手動實現尸诽。具體的還得看實際業(yè)務的需求甥材。
5. 如何避免消息的重復消費和重復投遞
在消息生產時,MQ內部針對每條生產者發(fā)送的消息生成一個inner-msg-id性含,作為去重和冪等的依據(消息投遞失敗并重傳)洲赵,避免重復的消息進入隊列;在消息消費時商蕴,要求消息體中必須要有一個bizId(對于同一業(yè)務全局唯一叠萍,如支付ID、訂單ID绪商、帖子ID等)作為去重和冪等的依據苛谷,避免同一條消息被重復消費
6. 消息如何分發(fā)(路由)
個人覺的消息分發(fā)和消息路由屬于同一問題,生產者將消息發(fā)送到Exchange上格郁,然后Exchange將消息路由到一個或者多個隊列上腹殿,如果路由不到,將消息根據發(fā)送者確認機制回傳給生產者例书,或者直接丟棄锣尉;消費者訂閱隊列上的消息,以上就是消息的流經的整個流程
- 生產者鏈接到RabbitMQ决采,建立一個Connection自沧,然后基于該Connection開啟Channel
- 生產者聲明一個Exchange,并設置相關屬性(Exchange類型fanout树瞭、direct拇厢、topic和headers爱谁、是否持久化)
- 生產者聲明一個Queue,并設置相關屬性(是否持久化)
- 生產者將消息發(fā)送給Exchange,一般會指定一個RoutingKey孝偎,用來指定這個消息的路由規(guī)則访敌,而這個RoutingKey需要與Exchange類型和BindingKey聯合作用才能最終生效
- 相應的Exchange根據接收到的消息的路由鍵查找匹配的隊列,如果匹配上將消息發(fā)送到隊列上衣盾,如果查詢不到根據發(fā)送者確認模式捐顷,是否返回給發(fā)送者或者丟掉
- 消費者鏈接到RabbitMQ,建立一個Connection雨效,然后基于該Connection開啟 Channel
- 消費者訂閱隊列迅涮,根據autoAck是否為false來決定是否手動確認消息
- RabbitMQ服務接收到消息確認,刪除隊列中該消息
7. 如何確保消息不丟失
消息丟失有以下三種情況:
- 生產者發(fā)送消息到RabbitMQ中徽龟,如果沒有對應Exchange叮姑、或者Exchange沒有匹配隊列,或者隊列沒有任何消費者都可能導致消息的丟失
對于發(fā)送失敗的据悔,我們可以使用生產者確認機制來讓發(fā)送失敗的消息回傳給生產者传透,或者使用備份交換機的方式來處理發(fā)送失敗的消息
- rabbitMQ服務重啟、關閉极颓、宕機情況下導致的消息丟失
RabbitMQ持久化包括三個部分:Exchange的持久化朱盐、Queue的持久化和Message的持久化
我們要持久化消息,則必須持久化Queue菠隆,因為Message是存儲在Queue上的兵琳,如果Queue不持久化的話,Message即便是持久化了骇径,重啟服務也會因為沒有存儲的載體導致Message的丟失
這里注意下躯肌,將所有的消息持久化,這樣會嚴重影響RabbitMQ的性能破衔,對于可靠性不是那么高的消息可以不采用持久化來提高系統(tǒng)整體的吞吐量
- 消費者設置autoAck為true,可能導致消費者還沒有來得及消費就宕機了清女,其實也是變相的消息丟失
這個需要我們在消費消息時,設置autoAck為true晰筛,同時注意解決消費異常的情況嫡丙,具體的參考面試題4