文章主要是兩部分,第一部分介紹 RabbitMQ 的基礎(chǔ)信息贸典,網(wǎng)上有很多视卢,如果對 RabbitMQ 比較熟悉可以略過,第二部分主要講述遇到的問題和影響性能的一些因素以及解決辦法廊驼。
第一部分
RabbitMQ 基礎(chǔ)介紹
RabbitMQ 是基于 Erlang 語言實(shí)現(xiàn) AMQP(高級消息隊(duì)列協(xié)議)的消息中間件的一種据过,最初起源于金融系統(tǒng),用于在分布式系統(tǒng)中存儲轉(zhuǎn)發(fā)消息妒挎,在易用性绳锅、擴(kuò)展性、高可用性等方面表現(xiàn)不俗饥漫。
RabbitMQ 主要是為了實(shí)現(xiàn)系統(tǒng)之間的雙向解耦而實(shí)現(xiàn)的榨呆,消息的發(fā)送者無需知道消息使用者的存在,反之亦然庸队。
一些 RabbitMQ 術(shù)語
- Channel
- Producer
- Exchange
- Queue
- Consumer
數(shù)據(jù)流
Channel
客戶端和服務(wù)建立連接积蜻,通過 Channel 向 Exchange 發(fā)送消息,Exchange 通過 Routing Key 將消息路由到 Queue (Exchange 和 Queue 通過 Routing Key 建立綁定關(guān)系彻消,Routing Key 也可以為空)竿拆。
Exchange (交換機(jī))
接收消息且轉(zhuǎn)發(fā)消息到綁定隊(duì)列,Producer 只能將消息發(fā)給 Exchange宾尚,由 Exchange 將消息轉(zhuǎn)發(fā)到綁定的 Queue丙笋,Exchange 常用的幾種類型工作模式谢澈,fanout、direct御板、topic锥忿。
fanout 模式即為廣播模式,Exchange 會(huì)把同一份消息發(fā)送給所有的綁定隊(duì)列怠肋,每一個(gè)隊(duì)列收到的消息是相同的敬鬓。
生產(chǎn)者 P 通過 Exchange X 將消息發(fā)給兩個(gè)隊(duì)列,C1笙各、C2 分別消費(fèi)對應(yīng)的隊(duì)列钉答,他們得到的數(shù)據(jù)是相同的。
direct 模式通過 RoutingKey 將消息發(fā)送給指定的隊(duì)列
生產(chǎn)者 P 通過 Exchange X 將 error 消息發(fā)送到 amqp.gen-S9b 隊(duì)列杈抢,將 info数尿、error、warning 消息發(fā)送到 amqp.gen-A1 隊(duì)列惶楼,他們分別對應(yīng)著消費(fèi)者 C1右蹦、C2。
topic 模式按照規(guī)則轉(zhuǎn)發(fā)鲫懒,跟 direct 差不多嫩实,只是但是支持模式匹配,通配符等窥岩,具有更好的靈活性甲献。
topic 模式下,會(huì)對 Routing key 做模式匹配颂翼,*
代表匹配一個(gè)單詞晃洒,#
匹配 0 個(gè)或多個(gè),*.orange.*
會(huì)把指定 Routing key 中間是 orange 的全部路由到 Q1 隊(duì)列朦乏,而 lazy.#
會(huì)把是 lazy.
開頭的消息分發(fā)到 Q2 隊(duì)列球及,*.*.rabbit
則代表把前兩個(gè)是任意詞,但是結(jié)尾是 .rabbit
的消息分發(fā)到 Q2 隊(duì)列呻疹。
交換機(jī)的幾個(gè)屬性
- Durabilty 是否持久化
- Auto Delete 是否自動(dòng)刪除
Queue(隊(duì)列)
隊(duì)列是通過 RoutingKey 和 Exchange 綁定在一起的吃引,Queue 的消息都來自 Exchange,Producer 是無法直接像隊(duì)列發(fā)送消息的刽锤,一個(gè)隊(duì)列可以和多個(gè) Exchange 綁定在一起镊尺。
Queue 的幾個(gè)屬性
- Durabilty
- Auto Delete
第二部分
問題:通過 MQ 監(jiān)控管理后臺發(fā)現(xiàn)隊(duì)列積壓嚴(yán)重,內(nèi)存消耗 6G 多并思。
使用場景:移動(dòng)端上報(bào)日志庐氮,存儲到 RabbitMQ,之后服務(wù)端消耗 MQ 對日志進(jìn)行格式化后再次分發(fā)宋彼。
注意上圖中的兩個(gè)紅色框處(當(dāng)時(shí)沒有截圖)弄砍,Consumer utilisation 當(dāng)時(shí)在 7% 左右仙畦,Process memory 大約 6G 多,生產(chǎn)者每秒大約產(chǎn)生 2000 條消息音婶,Consumer utilisation 代表了消費(fèi)者的工作效率慨畸,一般效率低有幾種情況
- 消費(fèi)者太少
- ACK 回執(zhí)速度太慢
- 消費(fèi)者太多
首先我們沒有采用 ACK 機(jī)制,這樣就不存在這個(gè)問題桃熄,第二 Exchange 和 Queue 在建立的時(shí)候先口,采用了 fanout 模式并且持久化,持久化和非持久化大約有 10 倍的性能差異瞳收,如果只是單存的針對同一個(gè)隊(duì)列增加 Consumer,并不會(huì)改善效率厢汹,而 fanout 的廣播模式不利于增加多個(gè) Queue螟深。
解決辦法:重建了 Exchange 和 Queue,采用 direct 模式且非持久化的方式烫葬,對同一個(gè) Exchange 綁定了 5 個(gè) Queue界弧,生產(chǎn)者隨機(jī)的將消息分發(fā)到某個(gè)隊(duì)列,每個(gè) Queue 會(huì)對應(yīng)一個(gè)消費(fèi)者搭综。因?yàn)橄⒈旧硎侨罩竟富罩居袝r(shí)間的,所以不存在時(shí)序的問題兑巾,這樣就大大的提高了消費(fèi)者的工作效率条获,通過這樣的改進(jìn)以后,Consomer utilisation 基本處在 100%蒋歌,而且也沒有出現(xiàn)隊(duì)列積壓帅掘。
內(nèi)存與流量控制參數(shù)
- prefetch 是每次從一次性從 broker 里面取的待消費(fèi)的消息的個(gè)數(shù),值太大會(huì)增加延遲堂油,太小會(huì)導(dǎo)致消息積壓修档。
- vm_memory_high_watermark 內(nèi)存流量控制,默認(rèn) 0.4(還可以是絕對值)府框,當(dāng)占用物理內(nèi)存的 40% 時(shí)吱窝,它會(huì)引起一個(gè)內(nèi)存報(bào)警并且阻塞所有連接。 百分比情況下可使用內(nèi)存
vm_memory_limit = vm_memory_high_watermark * 物理內(nèi)存
迫靖,絕對值情況下vm_memory_limit = vm_memory_high_watermark
院峡。
$ rabbitmqctl status | grep vm_memory
{vm_memory_high_watermark,0.4},
{vm_memory_limit,40530786713},
- vm_memory_high_watermark_paging_ratio 確定了何時(shí)執(zhí)行消息從內(nèi)存轉(zhuǎn)移到硬盤,默認(rèn) 0.5袜香,當(dāng)內(nèi)存消耗 vm_memory_limit * 0.5 時(shí)撕予,開始從內(nèi)存轉(zhuǎn)移到硬盤。
PS:
使用 RabbitMQ 時(shí)蜈首,創(chuàng)建 Exchange 和 Queue 要注意自己的使用場景实抡,是否需要持久化欠母,是否需要 ACK 機(jī)制,消息分發(fā)模式吆寨,是否有時(shí)序要求等等赏淌,千萬不要隨便建個(gè) fanout 模式放那就用。
參考
https://www.rabbitmq.com/documentation.html
https://emacsist.github.io/tags/#rabbitmq
http://lynnkong.iteye.com/blog/1699684
https://www.gitbook.com/book/geewu/rabbitmq-quick