RabbitMQ消息模型概覽(簡明教程)

     小菜最近用到RabbitMQ,因爲以前瞭解過其餘消息中間件,算是有些基礎,因此隨手從網上搜了幾篇文章,準備大概瞭解下RabbitMQ的消息模型,沒想到網上文章千篇一概,寫一大堆內容,就是說不明白到底怎麼回事,真是逼小菜寫博客…java

     首先說明本文只適合有消息中間件基礎的讀者,本文不會講解基礎概念,而是一針見血的指明RabbitMQ該怎麼用,告訴讀者RabbitMQ能作什麼,而不是像網絡上其餘文章那樣花裏胡哨抓不住重點。nginx

     好了,直入正題。redis

 

simple簡單隊列安全

 

     這種隊列,純屬RabbitMQ搞的一個花樣,僅僅是個概念而已!並非實際的隊列類型!他就是在假設某個隊列只有一個消費者,也就是說,讀者在實際使用中,某個隊列傻傻的只用一個消費者去消費,這就叫simple簡單隊列啦,應用場景極少,通常狀況下消費端都會有多個消費者。網絡

 

Fair dispatch公平分發多線程

 

     這種隊列讓人一看,有點蒙逼,實際上這個概念很是很是簡單,若是讀者用過redis的話,這個隊列模式很像redis的list用法,只不過redis是拉取模型,而mq是推送模型。併發

     這個公平,可不是說消息平均的發送給消費者,偏偏相反,消費者消費消息的多少,徹底取決於消費者的處理能力,能者多勞,至關於消費者主動從mq中取消息,而不是被mq安排消息。負載均衡

     實現上也不難理解,消費端消費數據時,會有一個確認消費完成的動做,mq收到消費完成的通知後,纔會繼續向該消費者發送消息,所以,若是消費者處理速度快,那麼最終mq向它發送的消息就多,若是消費者處理的慢,mq向它發送的消息就少。性能

     在這小菜貼出java代碼實現的關鍵點:優化

RabbitMQ Fair dispatch公平分發

     固然,這是消費端代碼,僅僅在消費端作處理便可,對於生產端來講是透明的,不須要作任何處理。

 

Round-robin輪詢分發

 

     所謂輪詢分發,就是公平分發的退化版,打開自動通知,去掉手動通知,去掉消費端消費條數限制,就是輪詢分發啦!!!

     其實輪詢分發就是利用了自動通知參數,開啓了自動通知,mq根據一個簡單的規則(好比取模運算),先肯定好哪些消息發送給哪些消費者,不管消費者處理能力如何,這些消息都得讓你處理,所以每一個消費者最終處理的消息數量,是相同的(忽略"消息數量/消費者"不能整除的狀況)。

     這種模式很明顯是有問題的,首先,這種模式不能很好的利用消費端的性能差別,作不到真正意義上的負載均衡,浪費資源;其次(只是猜想),這種模型還有可能形成大量消息堆積在消費者容器中,這是很是危險的,不只會形成消息丟失,還有可能壓垮消費者。

 

publish_subscribe發佈訂閱模式

 

     RabbitMQ 中有一個交換機Exchanges的概念,發佈訂閱就是經過交換機實現的。

     交換機的概念很是簡單,就是一個轉發器,有了交換機以後,生產端先把消息發送到交換機,而後交換機再把消息發送到與其綁定的消息隊列,這樣就解決了生成端如何把一條消息批量發送到多個消息隊列的問題。

     交換機自己沒有數據存儲能力,僅僅是一個代理,能夠理解成nginx。

     所以,實現發佈訂閱的關鍵在於:

 

          ·  生產端(發佈端)直接發送消息到交換機,而不是具體的消息隊列。

          ·  多個消費端(訂閱端)將本身的消息隊列綁定到同一個交換機上。

 

     這樣就實現了發佈訂閱。

 

routing路由模式

 

     路由模式僅僅基於發佈訂閱搞了一點小事情,在發佈訂閱模式中,交換機無腦向全部與之綁定的消息隊列發送消息,而路由模式對交換機作了一些限制,它指定了一個route key,生產端向交換機發送消息時,指定消息的route key,消費端將消息隊列綁定到交換機時,也指定該隊列消費的route key,這樣一來,交換機就能夠根據消息的route key,將該消息轉發到綁定(消費)該route key的消息隊列。

     生產端關鍵點:

RabbitMQ routing路由模式生產者 

     消費端關鍵點:

 RabbitMQ routing路由模式消費者

 

topic主題模式

 

     RabbitMQ又開始搞花樣了,咋一看topic小菜還覺得是kafka裏的topic概念呢,弄的莫名其妙。

     主題模式其實就是路由模式的一個增強,並且是很是很是很是簡單的一個增強:route key支持通配符。

     主題模式和路由模式徹底同樣,只不過是消費端route key不用寫死,增長了一個模糊匹配的功能,這樣在某些場景下,消費端就不用逐一綁定全部監聽的route key,直接用抽象的通配符表示便可,固然,這是針對消費端的優化,與生產端無關。

 

關於RabbitMQ的可靠性

 

     消費端的消費可靠性,已經在"Fair dispatch公平分發"章節中作了介紹,即利用手動通知告訴mq消費成功,但通知也有不可達的可能,進而涉及到重發,具體的處理細節,讀者自行查閱資料。

     生產端的提交可靠性,能夠經過mq的回調機制實現,即生產端發送消息時本身維護一份已發送消息的集合,mq收到某條消息以後,會向生產端發送一個接收成功確認(體如今代碼中就是回調),而後生產端根據確認消息的惟一id,從本身維護的已發送消息集合中移除該消息,從而確保每條消息都成功發送到了mq。

     假如某些消息未成功到達mq,那麼就不會有對應消息的確認,最終集合中會有剩餘元素(理想狀況下是沒有的),這些剩餘元素,就是發送失敗的消息,須要重發。

     簡單展現下代碼關鍵點( 生產端):

 RabbitMQ消息可靠性

 

     雖然確認機制能夠保證消息的可靠性,可是必然帶來性能損失,所以到底需不須要開啓生產端或消費端的確認機制,須要根據業務場景具體分析。

 

一些注意事項

 

     RabbitMQ的Connection是昂貴的,但Channel是廉價的,在多線程環境下,儘可能建立少數Connection,而後在每一個Connection中建立多個Channel,利用Channel實現Connection複用,從而提升系統性能。很像Java NIO裏的Selector到Channel的多路複用。

     生產端發送消息時,同一個Channel的basicPublish方法並非線程安全的,所以更加體現出多Channel的重要性。若是生產端須要使用多線程發送消息,那麼必須建立多個Channel,每個線程單獨使用一個Channel,可是這些Channel能夠來自同一個Connection。假如線程數量過多,那麼也不能夠無限制的建立Channel,須要使用Channel Pool(鏈接池)的思路去控制併發。

     對於同一個Channel而言,發送消息和接受消息是互不影響的,能夠進行併發操做。

 

吐槽

 

     最後吐槽一下RabbitMQ客戶端API設計的真難用,同一個API居然經過參數值重載,就好比向消息隊列發送消息是這樣:

 

 channel.basicPublish("","隊列名稱", null, message.getBytes()); 

 

     而後向交換機發消息是這樣:

 

 channel.basicPublish("交換機名稱","route key", null, message.getBytes()); 

 

     同一個方法,第二個參數的含義,居然是經過第一個參數是否爲空決定的,厲害了~

相關文章
相關標籤/搜索