1.RabbitMQ簡介
1.基于AMQP協(xié)議Erlang語言開發(fā)的一款消息中間件,客戶端語言支持比較多,
比如Python,Java,Ruby,PHP,JS,Swift.運(yùn)維簡單,靈活路由,但是性能不高,
可以滿足一般場景下的業(yè)務(wù)需要,三高場景下吞吐量不高,消息持久化沒有采取
零拷貝技術(shù),消息堆積時(shí),性能會(huì)下降
2.消息吞吐量在1w~10w級
3.沒有消費(fèi)者組的概念,需要依賴Exchange和隊(duì)列之間的綁定關(guān)系
2.模型設(shè)計(jì)圖
3.特點(diǎn)
1.保證可靠性。RabbitMQ使用一些機(jī)制來保證可靠性。如持久化、傳輸確認(rèn)、發(fā)布確認(rèn)等
2.具有靈活的路由功能。在消息進(jìn)入隊(duì)列之前,是通過Exchange(交換器)來路由消息的。
對于典型的路由功能,針對更復(fù)雜的路由功能,可以將多個(gè)Exchange綁定在一起,也可以通過插件機(jī)制
來實(shí)現(xiàn)自己的Exchange
3.支持多種協(xié)議.RabbitMQ除了支持AMQP協(xié)議之外,還通過插件的方式支持其他消息隊(duì)列協(xié)議,比如STOMP,MQTT等
4.支持多語言客戶端.RabbitMQ幾乎支持所有常用的語言,比如Java、.NET、Ruby等
5.提供管理界面.RabbitMQ提供了一個(gè)易用的用戶界面,使得用戶可以監(jiān)控和管理消息Broker的許多方面
6.提供跟蹤機(jī)制.RabbitMQ提供了消息跟蹤機(jī)制。如果消息異常,使用者可以查出發(fā)生了什么情況
7.提供插件機(jī)制.RabbitMQ提供了許多插件,從多方面進(jìn)行擴(kuò)展,也可以編寫自己的插件
4.核心組件
4.1 VirtualHost(虛擬主機(jī))
可以在一個(gè)RabbitMQ集群中劃分出多個(gè)虛擬主機(jī),每個(gè)虛擬主機(jī)都有AMQP的全套基礎(chǔ)組件,
并且可以針對每個(gè)虛擬主機(jī)進(jìn)行權(quán)限以及數(shù)據(jù)分配,并且不同虛擬主機(jī)之間是完全隔離的
4.2 Connection(連接)
客戶端與RabbitMQ進(jìn)行交互,首先就需要建立一個(gè)TCP連接
4.3 Channel(信道)
一旦客戶端與RabbitMQ建立了連接,就會(huì)分配一個(gè)AMQP信道Channel.每個(gè)信道都會(huì)被分配一個(gè)唯一的ID
也可以理解為是客戶端與RabbitMQ實(shí)際進(jìn)行數(shù)據(jù)交互的通道
RabbitMQ為了減少性能開銷,也會(huì)在一個(gè)Connection中建立多個(gè)Channel,這樣便于客戶端進(jìn)行多線程連接
這些連接會(huì)復(fù)用同一個(gè)Connection的TCP通道
4.4 Exchange(交換機(jī))
這是RabbitMQ中進(jìn)行數(shù)據(jù)路由的重要組件。消息發(fā)送到RabbitMQ中后,會(huì)首先進(jìn)入一個(gè)交換機(jī)
然后由交換機(jī)負(fù)責(zé)將數(shù)據(jù)轉(zhuǎn)發(fā)到不同的隊(duì)列中。RabbitMQ中有多種不同類型的交換機(jī)來支持不同的路由策略
Exchange可以持久化
4.4.1 交換器類型
Direct
如果消息中的路由鍵(RoutingKey)和Binding中的綁定鍵(binding key)一致,
交換器就將消息發(fā)送到對應(yīng)的隊(duì)列中。路由鍵與隊(duì)列名稱要完全匹配,
如果將一個(gè)隊(duì)列綁定到交換機(jī)要求路由鍵(RoutingKey)為dog,則只轉(zhuǎn)發(fā)RoutingKey標(biāo)記為dog
的消息,不會(huì)轉(zhuǎn)發(fā)dog.puppy消息,也不會(huì)轉(zhuǎn)發(fā)dog.guard消息等
Direct交換器是完全匹配、單播的模式Fanout
Fanout交換器不處理路由鍵,只是簡單地將隊(duì)列綁定到交換器,發(fā)送到交換器地每條消息都會(huì)被
轉(zhuǎn)發(fā)到與該交換器綁定的所有隊(duì)列中,這很像子網(wǎng)廣播,子網(wǎng)內(nèi)的每個(gè)主機(jī)都獲得了一份賦值的消息Topic
Topic交換器通過模式匹配分配消息的路由鍵屬性,將路由鍵和某種模式進(jìn)行匹配。
此時(shí)隊(duì)列需要綁定一種模式,Topic交換器將路由鍵和綁定鍵的字符串切分成單詞,這些單詞可以用".“隔開
該交換器會(huì)識別兩個(gè)通配符”#“和”“,其中”#“匹配0個(gè)或者多個(gè)單詞”"匹配不多不少一個(gè)單詞Headers
Headers交換器匹配AMPQ消息的Header而不是路由鍵,此外Header交換器和Direct交換器完全一致
但是性能相差很多,目前幾乎不用了
4.5 Bingding(綁定)
用于消息隊(duì)列和交換器之間的關(guān)聯(lián)。一個(gè)綁定就是基于路由鍵將交換器和消息隊(duì)列連接起來的路由規(guī)則
可以將交換器理解成一個(gè)由綁定構(gòu)成的路由表
5.支持的消息類型
普通消息
無序消息,效率最高順序消息
利用隊(duì)列的FIFO屬性,保證一個(gè)Exchange只能路由到一個(gè)隊(duì)列上,可以實(shí)現(xiàn)順序消費(fèi),但是性能不會(huì)很高延時(shí)消息
設(shè)置消息的ttl,當(dāng)這個(gè)消息死亡時(shí),進(jìn)入到死信隊(duì)列,可以實(shí)現(xiàn),原生不支持死信隊(duì)列
第一種,消息無法消費(fèi)成功.第二種,消息已經(jīng)過期
6.Producer生產(chǎn)消息
6.1 消息確認(rèn)機(jī)制
6.1.1 單向發(fā)送
不需要確認(rèn),效率最高,也最容易丟消息
6.1.2 同步確認(rèn)
單條消息確認(rèn)
Producer每發(fā)送一條信息,等待收到確認(rèn)之后再發(fā)送下一條,消息最可靠,性能不高批量消息確認(rèn)
當(dāng)批量消息中有一條消息出錯(cuò)時(shí),整批消息都需要重傳,將會(huì)增大重復(fù)消費(fèi)的可能
6.1.3 異步確認(rèn)
通過回調(diào)接口來判斷哪些消息被確認(rèn)收到,消息最高,實(shí)現(xiàn)最復(fù)雜
7.Consumer消費(fèi)消息
7.1 Pull拉取消息
由Consumer主動(dòng)向RabbitMQ拉取消息,請求由consumer發(fā)起,broker的壓力相對來說會(huì)比較小
7.2 Push推送消息
由RabbitMQ Server主動(dòng)向Consumer推送消息,請求由broker發(fā)起,broker壓力會(huì)比較大
7.3 消費(fèi)消息確認(rèn)機(jī)制
7.3.1 自動(dòng)提交
消費(fèi)者接收到消息時(shí),自動(dòng)地向broker進(jìn)行提交,容易造成消息丟失
7.3.2 手動(dòng)提交
由消費(fèi)者手動(dòng)在合適的場景下進(jìn)行提交,更靈活,不容易造成消息沒有消費(fèi)到而丟失
7.3.3 拒絕消息
拒絕一條消息/拒絕多條消息.當(dāng)消費(fèi)者處理消息失敗或者當(dāng)前不能處理該消息時(shí),可以給Broker發(fā)送一個(gè)拒絕消息的指令.并且可要求Broker將該消息丟棄或者重新放入隊(duì)列中.需要注意的是,當(dāng)隊(duì)列中只有一個(gè)消費(fèi)者時(shí),需要確認(rèn)不會(huì)因?yàn)榫芙^消息并選擇重新放入隊(duì)列中而導(dǎo)致消息在同一個(gè)消費(fèi)者上發(fā)生死循環(huán)
7.4 消息預(yù)取
在實(shí)際場景中,如果對每條消息的處理時(shí)間不同,可能導(dǎo)致有些消費(fèi)者一直很忙,而有些消費(fèi)者
處理很快并一直空間,這時(shí)可以通過設(shè)置預(yù)取數(shù)量(PrefetchCount)限制每個(gè)消費(fèi)者在收到下一個(gè)確認(rèn)回直前
一次最多可以接收到多少條消息
7.5 流控機(jī)制
當(dāng)生產(chǎn)消息速度大于消費(fèi)速度時(shí),會(huì)造成隊(duì)列中堆積大量消息,服務(wù)端默認(rèn)配置是當(dāng)內(nèi)存使用達(dá)到40%,磁盤空間小于50M時(shí),會(huì)觸發(fā).RabbitMQ可以對內(nèi)存的使用量設(shè)置閾值,當(dāng)達(dá)到閾值后生產(chǎn)者將被阻塞,,直到對相應(yīng)資源的使用回復(fù)正常除了這兩個(gè)閾值外,RabbitMQ還用流控(FlowControl)機(jī)制來確保穩(wěn)定性。由于Erlang進(jìn)程之間并不共享內(nèi)存(binaries類型),而是通過傳遞消息來通信的,所以每個(gè)進(jìn)程都有自己的進(jìn)程郵箱(mailbox),因?yàn)镋rlang默認(rèn)不會(huì)對mailbox的大小進(jìn)行設(shè)限所以如果由大量消息持續(xù)發(fā)往某個(gè)進(jìn)程,將會(huì)導(dǎo)致該mailbox過大,最終內(nèi)存溢出,進(jìn)程崩潰在RabbitMQ中如果生產(chǎn)者持續(xù)高速發(fā)送消息,而消費(fèi)者消費(fèi)的速度又低于生產(chǎn)者發(fā)送的速度,若沒有流控很快就會(huì)使mailbox達(dá)到閾值限制,從而阻塞生產(chǎn)者的操作(因?yàn)橛蠦lock機(jī)制,所以進(jìn)程不會(huì)崩潰),然后RabbitMQ會(huì)進(jìn)行換頁操作,把內(nèi)存中的數(shù)據(jù)持久化到磁盤上觸發(fā)流控機(jī)制后RabbitMQ服務(wù)端接收消息的速度就會(huì)變慢,從而使進(jìn)入隊(duì)列的消息減少,同時(shí)RabbitMQ服務(wù)端的消息推送也會(huì)收到極大的影響,服務(wù)器端推送的頻率會(huì)大幅下降
8.RabbitMQ常見問題
8.1 如何保證順序消費(fèi)
單隊(duì)列+單消息。
RabbitMQ當(dāng)中,針對消息順序的設(shè)計(jì)是比較弱的。唯一比較好的策略就是單隊(duì)列+單消息推送。
即一組有序消息,只發(fā)到一個(gè)隊(duì)列中,利用隊(duì)列的FIFO特性保證消息在隊(duì)列內(nèi)順序不會(huì)亂.
但是這是以極度消耗性能作為代價(jià)的。在業(yè)務(wù)場景中,應(yīng)該盡量避免這種場景。
然后在消費(fèi)者進(jìn)行消費(fèi)時(shí),保證只有一個(gè)消費(fèi)者。同時(shí)指定prefetch屬性為1
問題:如果生產(chǎn)者發(fā)送的消息123,結(jié)果1消息發(fā)送失敗了,進(jìn)行重試發(fā)送,再進(jìn)行到broker中接收時(shí)則會(huì)變成231,盡可能不要異步發(fā)送,改成同步發(fā)送
8.2 如何避免重復(fù)消費(fèi)
無法做到絕對的消息不重復(fù)
8.2.1 Producer
- 如果采用同步發(fā)送確認(rèn)機(jī)制。生產(chǎn)者發(fā)送消息之后,要確保一條消息已經(jīng)發(fā)送Broker
- 如果采用的是異步發(fā)送確認(rèn)機(jī)制。需要保證每個(gè)消息具有唯一的id,可以供Producer查詢到這條消息是否發(fā)送到了Broker
- 盡量不要采用批量消息發(fā)送,如果其中某一條消息存在問題,則會(huì)導(dǎo)致整批消息都需要重傳
- 消息去重,增加唯一id
- 給消息增加過期時(shí)間,如果在消息存活期內(nèi)沒有被消費(fèi),則進(jìn)入死信隊(duì)列
- 問題:有可能Broker已經(jīng)收到了消息,由于網(wǎng)絡(luò)抖動(dòng),網(wǎng)絡(luò)向Producer返回的確認(rèn)ACK,Producer暫未收到,Producer先于Broker重發(fā)了消息
8.2.2 Consumer
- 盡可能地做到冪等消費(fèi)
- 逐條消費(fèi)消息,手動(dòng)確認(rèn)
- 首先要確保消息是可以正常被消費(fèi)的,不然會(huì)進(jìn)入重試隊(duì)列,一直被消費(fèi),直到進(jìn)入死信隊(duì)列中
- 給消息增加一個(gè)唯一標(biāo)識,消費(fèi)消息時(shí),在業(yè)務(wù)當(dāng)中判斷一下改消息是否被消費(fèi)過
- 問題:Consumer已經(jīng)消費(fèi)完了消息,但是在手動(dòng)提交位點(diǎn)時(shí),發(fā)生了宕機(jī),這時(shí)Broker將消息轉(zhuǎn)發(fā)到了其他的Consumer上,造成了消息的重復(fù)消費(fèi)
8.2.3 Broker
- 隊(duì)列設(shè)置為持久化,避免消費(fèi)過的消息位點(diǎn)沒有持久化到磁盤上而重復(fù)消費(fèi)
8.3 如何避免消息丟失
步驟(1,2,3,4)都有可能造成消息的丟失,
無法做到絕對的消息不丟失
8.3.1 Producer
消息確認(rèn)模式調(diào)整為同步模式文章來源:http://www.zghlxwxcb.cn/news/detail-804006.html
8.3.2 Consumer
消費(fèi)消息時(shí)逐條消息消費(fèi),并且在業(yè)務(wù)執(zhí)行完畢時(shí)進(jìn)行手動(dòng)提交文章來源地址http://www.zghlxwxcb.cn/news/detail-804006.html
8.3.3 Broker
- 將隊(duì)列聲明為持久化
- 主從集群,每條消息等待其他所有的slave節(jié)點(diǎn)復(fù)制完成,可以等到大多數(shù)節(jié)點(diǎn)
- 調(diào)整Broker刷盤的緩沖區(qū)大小,縮小刷盤間隔,爭取讓更多的消息落在磁盤上,防止內(nèi)存中的數(shù)據(jù)丟失
8.4 消息堆積如何解決
8.4.1 Producer
- 如果業(yè)務(wù)允許生產(chǎn)速度降低,則可以進(jìn)行使用,大多數(shù)場景下,不會(huì)降低生產(chǎn)者生產(chǎn)消息的速度
- 消息確認(rèn)機(jī)制,盡可能地使用批量消息確認(rèn)或者異步確認(rèn),增大吞吐量,讓消費(fèi)者做到冪等
8.4.2 Consumer
- 增加消費(fèi)者數(shù)量,可以提升消息消費(fèi)的速度
- 增加單臺(tái)消費(fèi)者拉取消息的數(shù)量,盡可能地一次性拉取多條消息
- 增加單臺(tái)消費(fèi)者地線程數(shù),并發(fā)消費(fèi)
- 單條消息調(diào)整為手動(dòng)批量提交,相比逐條消息提交性能會(huì)比較好
8.4.3 Broker
- 可以使用懶加載隊(duì)列的方式,先存入到磁盤,使用到的時(shí)候再加載到內(nèi)存中
要付出一定的磁盤IO性能 - 消息推送模式由Push模式調(diào)整為Poll模式,降低Broker的壓力
到了這里,關(guān)于消息中間件之RabbitMQ的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!