五、RabbitMQ
1.rabbitmq 的使用場景有哪些餐茵?
- 異步處理
比如發(fā)短信和發(fā)送郵件科阎,就可以先把信息存入數(shù)據(jù)庫,然后寫入消息隊列忿族。通過消費消息去發(fā)送短信和發(fā)送郵件锣笨。 - 應(yīng)用解耦
訂單系統(tǒng):用戶下單后,訂單系統(tǒng)完成持久化處理,將消息寫入消息隊列,返回用戶訂單下單成功。
庫存系統(tǒng):訂閱下單的消息,獲取下單消息,進(jìn)行庫操作道批。
2.rabbitmq 有哪些重要的角色错英?
生產(chǎn)者:消息的創(chuàng)建者,負(fù)責(zé)創(chuàng)建和推送數(shù)據(jù)到消息服務(wù)器
消費者:消息的接收方隆豹,用于處理數(shù)據(jù)和確認(rèn)消息
代理:就是RabbitMQ本身椭岩,用于扮演快遞的角色,本身并不生產(chǎn)消息
3.rabbitmq 有哪些重要的組件璃赡?
Server:又稱為broker簿煌,接受客戶端連接,RabbitMQ 節(jié)點鉴吹;
Connection: 連接,應(yīng)用程序與brokder建立網(wǎng)絡(luò)連接;
channel:網(wǎng)絡(luò)通道,幾乎所有的操作都是在channel中進(jìn)行的惩琉,是進(jìn)行消息對象的通道豆励,客戶端可以建立 多個通道,每一個channel表示一個會話任務(wù)
Virtual Host:虛擬機,一個節(jié)點由若干個虛擬機組成良蒸;
Exchange:交換機技扼,一個虛擬機由若干個交換機組成;
Queue:消息隊列嫩痰,和交換機通過routing key綁定
4.rabbitmq 中 vhost 的作用是什么剿吻?
vhost本質(zhì)上是一個mini版的RabbitMQ服務(wù)器,擁有自己的隊列串纺、綁定丽旅、交換器和權(quán)限控制;
vhost通過在各個實例間提供邏輯上分離纺棺,允許你為不同應(yīng)用程序安全保密地運行數(shù)據(jù)榄笙;
vhost是AMQP概念的基礎(chǔ),必須在連接時進(jìn)行指定祷蝌,RabbitMQ包含了默認(rèn)vhost:“/”茅撞;
當(dāng)在RabbitMQ中創(chuàng)建一個用戶時,用戶通常會被指派給至少一個vhost巨朦,并且只能訪問被指派vhost內(nèi)的隊列米丘、交換器和綁定,vhost之間是絕對隔離的糊啡。
vhost可以理解為虛擬broker拄查,即mini-RabbitMQ server,其內(nèi)部均含有獨立的queue悔橄、bind靶累、exchange等,最重要的是擁有獨立的權(quán)限系統(tǒng)癣疟,可以做到vhost范圍內(nèi)的用戶控制挣柬。當(dāng)然,從RabbitMQ全局角度睛挚,vhost可以作為不同權(quán)限隔離的手段(一個典型的例子邪蛔,不同的應(yīng)用可以跑在不同的vhost中)。
5.rabbitmq 的消息是怎么發(fā)送的扎狱?
生產(chǎn)者把生產(chǎn)的消息通過channel發(fā)送到Exchange上侧到,Exchange通過綁定的router key來選擇Queue,消費者監(jiān)聽到Queue上有新的消息淤击,就消費調(diào)此消息匠抗;
6.rabbitmq 怎么避免消息丟失?
-
消息持久化
RabbitMQ 的消息默認(rèn)存放在內(nèi)存上面污抬,如果不特別聲明設(shè)置汞贸,消息不會持久化保存到硬盤上面的绳军,如果節(jié)點重啟或者意外crash掉,消息就會丟失矢腻。
所以就要對消息進(jìn)行持久化處理门驾。如何持久化,下面具體說明下:
要想做到消息持久化多柑,必須滿足以下三個條件奶是,缺一不可。
1) Exchange 設(shè)置持久化
2)Queue 設(shè)置持久化
3)Message持久化發(fā)送:發(fā)送消息設(shè)置發(fā)送模式deliveryMode=2竣灌,代表持久化消息 -
ACK確認(rèn)機制
多個消費者同時收取消息聂沙,比如消息接收到一半的時候,一個消費者死掉了(邏輯復(fù)雜時間太長帐偎,超時了或者消費被停機或者網(wǎng)絡(luò)斷開鏈接)逐纬,如何保證消息不丟?
這個使用就要使用Message acknowledgment 機制削樊,就是消費端消費完成要通知服務(wù)端豁生,服務(wù)端才把消息從內(nèi)存刪除。
這樣就解決了漫贞,及時一個消費者出了問題甸箱,沒有同步消息給服務(wù)端,還有其他的消費端去消費迅脐,保證了消息不丟的case芍殖。 -
設(shè)置集群鏡像模式
1)單節(jié)點模式:最簡單的情況,非集群模式谴蔑,節(jié)點掛了豌骏,消息就不能用了。業(yè)務(wù)可能癱瘓隐锭,只能等待窃躲。
2)普通模式:默認(rèn)的集群模式,某個節(jié)點掛了钦睡,該節(jié)點上的消息不能用蒂窒,有影響的業(yè)務(wù)癱瘓,只能等待節(jié)點恢復(fù)重啟可用(必須持久化消息情況下)荞怒。
3)鏡像模式:把需要的隊列做成鏡像隊列洒琢,存在于多個節(jié)點,屬于RabbitMQ的HA方案
-消息補償機制
消息補償機制需要建立在消息要寫入DB日志褐桌,發(fā)送日志衰抑,接受日志,兩者的狀態(tài)必須記錄荧嵌。
然后根據(jù)DB日志記錄check 消息發(fā)送消費是否成功停士,不成功挖帘,進(jìn)行消息補償措施,重新發(fā)送消息處理恋技。
7.要保證消息持久化成功的條件有哪些?
聲明隊列必須設(shè)置持久化 durable 設(shè)置為 true.
消息推送投遞模式必須設(shè)置持久化逻族,deliveryMode 設(shè)置為 2(持久)蜻底。
消息已經(jīng)到達(dá)持久化交換器。
消息已經(jīng)到達(dá)持久化隊列聘鳞。
8.rabbitmq 持久化有什么缺點薄辅?
持久化的缺地就是降低了服務(wù)器的吞吐量,因為使用的是磁盤而非內(nèi)存存儲抠璃,從而降低了吞吐量站楚。
9.rabbitmq 有幾種廣播類型?
direct(默認(rèn)方式):最基礎(chǔ)最簡單的模式搏嗡,發(fā)送方把消息發(fā)送給訂閱方窿春,如果有多個訂閱者,默認(rèn)采取輪詢的方式進(jìn)行消息發(fā)送采盒。
headers:與 direct 類似旧乞,只是性能很差,此類型幾乎用不到磅氨。
fanout:分發(fā)模式尺栖,把消費分發(fā)給所有訂閱者。
topic:匹配訂閱模式烦租,使用正則匹配到消息隊列延赌,能匹配到的都能接收到。
10.rabbitmq 怎么實現(xiàn)延遲消息隊列叉橱?
消息的TTL(Time To Live)
消息的TTL就是消息的存活時間挫以。RabbitMQ可以對隊列和消息分別設(shè)置TTL。對隊列設(shè)置就是隊列沒有消費者連著的保留時間赏迟,也可以對每一個單獨的消息做單獨的設(shè)置屡贺。超過了這個時間,我們認(rèn)為這個消息就死了锌杀,稱之為死信甩栈。如果隊列設(shè)置了,消息也設(shè)置了糕再,那么會取小的量没。所以一個消息如果被路由到不同的隊列中,這個消息死亡的時間有可能不一樣(不同的隊列設(shè)置)突想。這里單講單個消息的TTL殴蹄,因為它才是實現(xiàn)延遲任務(wù)的關(guān)鍵究抓。
Dead Letter Exchanges
Exchage的概念在這里就不在贅述,一個消息在滿足如下條件下袭灯,會進(jìn)死信路由刺下,記住這里是路由而不是隊列,一個路由可以對應(yīng)很多隊列稽荧。
- 一個消息被Consumer拒收了橘茉,并且reject方法的參數(shù)里requeue是false。也就是說不會被再次放在隊列里姨丈,被其他消費者使用畅卓。
- 上面的消息的TTL到了,消息過期了蟋恬。
- 隊列的長度限制滿了翁潘。排在前面的消息會被丟棄或者扔到死信路由上。
Dead Letter Exchange其實就是一種普通的exchange歼争,和創(chuàng)建其他exchange沒有兩樣拜马。只是在某一個設(shè)置Dead Letter Exchange的隊列中有消息過期了,會自動觸發(fā)消息的轉(zhuǎn)發(fā)矾飞,發(fā)送到Dead Letter Exchange中去一膨。
實現(xiàn)延遲隊列
延遲任務(wù)通過消息的TTL和Dead Letter Exchange來實現(xiàn)。我們需要建立2個隊列洒沦,一個用于發(fā)送消息豹绪,一個用于消息過期后的轉(zhuǎn)發(fā)目標(biāo)隊列。
實現(xiàn)延遲隊列
延遲任務(wù)通過消息的TTL和Dead Letter Exchange來實現(xiàn)申眼。我們需要建立2個隊列瞒津,一個用于發(fā)送消息,一個用于消息過期后的轉(zhuǎn)發(fā)目標(biāo)隊列括尸。
生產(chǎn)者輸出消息到Queue1巷蚪,并且這個消息是設(shè)置有有效時間的,比如60s濒翻。消息會在Queue1中等待60s屁柏,如果沒有消費者收掉的話,它就是被轉(zhuǎn)發(fā)到Queue2有送,Queue2有消費者淌喻,收到,處理延遲任務(wù)雀摘。
11.如何保證消息消費時的冪等性裸删?
實際上我們只要保證多條相同的數(shù)據(jù)過來的時候只處理一條或者說多條處理和處理一條造成的結(jié)果相同即可,但是具體怎么做要根據(jù)業(yè)務(wù)需求來定阵赠,例如入庫消息涯塔,先查一下消息是否已經(jīng)入庫啊或者說搞個唯一約束啊什么的肌稻,還有一些是天生保證冪等性就根本不用去管,例如redis就是天然冪等性匕荸。
還有一個問題爹谭,消費者消費消息的時候在某些場景下要放過消費不了的消息,遇到消費不了的消息通過日志記錄一下或者搞個什么措施以后再來處理每聪,但是一定要放過消息旦棉,因為在某些場景下例如spring-rabbitmq的默認(rèn)回饋策略是出現(xiàn)異常就沒有提交ack,導(dǎo)致了一直在重發(fā)那條消費異常的消息药薯,而且一直還消費不了。
12.如何保證消息的可靠性傳輸救斑?
生產(chǎn)者弄丟了數(shù)據(jù)
生產(chǎn)者發(fā)送數(shù)據(jù)之前開啟RabbitMQ事務(wù)(channel.txSelect)童本,然后發(fā)送消息,如果消息沒有成功被RabbitMQ接收到脸候,那么生產(chǎn)者會收到異常報錯穷娱,此時就可以回滾事務(wù)(channel.txRollback),然后重試發(fā)送消息运沦;如果收到了消息泵额,那么可以提交事務(wù)(channel.txCommit)。但是問題是携添,RabbitMQ事務(wù)機制一搞嫁盲,基本上吞吐量會下來,因為太耗性能烈掠。
所以一般來說羞秤,如果你要確保說寫RabbitMQ的消息別丟,可以開啟confirm模式左敌,在生產(chǎn)者那里設(shè)置開啟confirm模式之后瘾蛋,你每次寫的消息都會分配一個唯一的id,然后如果寫入了RabbitMQ中矫限,RabbitMQ會給你回傳一個ack消息哺哼,告訴你說這個消息ok了。如果RabbitMQ沒能處理這個消息叼风,會回調(diào)你一個nack接口取董,告訴你這個消息接收失敗,你可以重試咬扇。而且你可以結(jié)合這個機制自己在內(nèi)存里維護每個消息id的狀態(tài)甲葬,如果超過一定時間還沒接收到這個消息的回調(diào),那么你可以重發(fā)懈贺。
事務(wù)機制和cnofirm機制最大的不同在于经窖,事務(wù)機制是同步的坡垫,你提交一個事務(wù)之后會阻塞在那兒,但是confirm機制是異步的画侣,你發(fā)送個消息之后就可以發(fā)送下一個消息冰悠,然后那個消息RabbitMQ接收了之后會異步回調(diào)你一個接口通知你這個消息接收到了。
所以一般在生產(chǎn)者這塊避免數(shù)據(jù)丟失配乱,都是用confirm機制的溉卓。
RabbitMQ弄丟了數(shù)據(jù)
開啟RabbitMQ的持久化功能。
設(shè)置持久化有兩個步驟搬泥,第一個是創(chuàng)建queue的時候?qū)⑵湓O(shè)置為持久化的桑寨,這樣就可以保證RabbitMQ持久化queue的元數(shù)據(jù),但是不會持久化queue里的數(shù)據(jù)忿檩;第二個是發(fā)送消息的時候?qū)⑾⒌膁eliveryMode設(shè)置為2尉尾,就是將消息設(shè)置為持久化的,此時RabbitMQ就會將消息持久化到磁盤上去燥透。必須要同時設(shè)置這兩個持久化才行沙咏,RabbitMQ哪怕是掛了,再次重啟班套,也會從磁盤上重啟恢復(fù)queue肢藐,恢復(fù)這個queue里的數(shù)據(jù)。
消費端弄丟了數(shù)據(jù)
這個時候得用RabbitMQ提供的ack機制吱韭,簡單來說吆豹,就是你關(guān)閉RabbitMQ自動ack,可以通過一個api來調(diào)用就行杉女,然后每次你自己代碼里確保處理完的時候瞻讽,再程序里ack一把。這樣的話熏挎,如果你還沒處理完速勇,不就沒有ack?那RabbitMQ就認(rèn)為你還沒處理完坎拐,這個時候RabbitMQ會把這個消費分配給別的consumer去處理烦磁,消息是不會丟的。
13.如何保證消息的順序性
拆分多個queue哼勇,每個queue一個consumer都伪,就是多一些queue而已,確實是麻煩點积担;或者就一個queue但是對應(yīng)一個consumer陨晶,然后這個consumer內(nèi)部用內(nèi)存隊列做排隊,然后分發(fā)給底層不同的worker來處理。