RabbitMQ基本概念
我們先看一下RabbitMQ模型結構圖,這樣會方便們更好地去理解RabbitMQ的基本原理。

通過上面這張應用相結合的結構圖既能夠清晰的看清楚整體的send Message到Receive Message的一個大致的流程。當然上面有很多名詞都相比還沒有介紹到,不要著急接下來我們就開始對其進行詳細的講解。
Queue
Queue(隊列)RabbitMQ的作用是存儲消息,隊列的特性是先進先出。上圖可以清晰地看到Client A和Client B是生產者,生產者生產消息最終被送到RabbitMQ的內部對象Queue中去,而消費者則是從Queue隊列中取出數(shù)據??梢院喕杀硎緸椋?/p>

生產者Send Message “A”被傳送到Queue中,消費者發(fā)現(xiàn)消息隊列Queue中有訂閱的消息,就會將這條消息A讀取出來進行一些列的業(yè)務操作。這里只是一個消費正對應一個隊列Queue,也可以多個消費者訂閱同一個隊列Queue,當然這里就會將Queue里面的消息平分給其他的消費者,但會存在一個問題:如果每個消息的處理時間不同,就會導致某些消費者一直在忙碌中,而有的消費者處理完了消息后一直處于空閑狀態(tài),因為前面已經提及到了Queue會平分這些消息給相應的消費者。這里我們就可以使用prefetchCount來限制每次發(fā)送給消費者消息的個數(shù)。詳情見下圖所示:

這里的prefetchCount=1是指每次從Queue中發(fā)送一條消息來。等消費者處理完這條消息后Queue會再發(fā)送一條消息給消費者。
Exchange
回憶一下,在開篇TabbitMQ消息模型圖中,消費者Client A和消費者Client B是如何知道我發(fā)送的消息是給Queue1還是給Queue2。首先明確一點就是生產者產生的消息并不是直接發(fā)送給消息隊列Queue的,而是要經過Exchange(交換器),由Exchange再將消息路由到一個或多個Queue,當然這里還會對不符合路由規(guī)則的消息進行丟棄掉,這里指的是后續(xù)要談到的Exchange Type。那么Exchange是怎樣將消息準確的推送到對應的Queue的呢?那么這里的功勞最大的當屬Binding,RabbitMQ是通過Binding將Exchange和Queue鏈接在一起,這樣Exchange就知道如何將消息準確的推送到Queue中去。簡單示意圖如下所示:

在綁定(Binding)Exchange和Queue的同時,一般會指定一個Binding Key,生產者將消息發(fā)送給Exchange的時候,一般會產生一個Routing Key,當Routing Key和Binding Key對應上的時候,消息就會發(fā)送到對應的Queue中去。那么Exchange有四種類型,不同的類型有著不同的策略。也就是表明不同的類型將決定綁定的Queue不同,換言之就是說生產者發(fā)送了一個消息,Routing Key的規(guī)則是A,那么生產者會將Routing Key=A的消息推送到Exchange中,這時候Exchange中會有自己的規(guī)則,對應的規(guī)則去篩選生產者發(fā)來的消息,如果能夠對應上Exchange的內部規(guī)則就將消息推送到對應的Queue中去。那么接下來就來詳細講解下Exchange里面類型。
Exchange Type
我來用表格來描述下類型以及類型之間的區(qū)別。
fanout
fanout類型的Exchange路由規(guī)則非常簡單,它會把所有發(fā)送到該Exchange的消息路由到所有與它綁定的Queue中。

上圖所示,生產者(P)生產消息1將消息1推送到Exchange,由于Exchange Type=fanout這時候會遵循fanout的規(guī)則將消息推送到所有與它綁定Queue,也就是圖上的兩個Queue最后兩個消費者消費。
direct
direct類型的Exchange路由規(guī)則也很簡單,它會把消息路由到那些binding key與routing key完全匹配的Queue中

當生產者(P)發(fā)送消息時Rotuing key=booking時,這時候將消息傳送給Exchange,Exchange獲取到生產者發(fā)送過來消息后,會根據自身的規(guī)則進行與匹配相應的Queue,這時發(fā)現(xiàn)Queue1和Queue2都符合,就會將消息傳送給這兩個隊列,如果我們以Rotuing key=create和Rotuing key=confirm發(fā)送消息時,這時消息只會被推送到Queue2隊列中,其他Routing Key的消息將會被丟棄。
topic
前面提到的direct規(guī)則是嚴格意義上的匹配,換言之Routing Key必須與Binding Key相匹配的時候才將消息傳送給Queue,那么topic這個規(guī)則就是模糊匹配,可以通過通配符滿足一部分規(guī)則就可以傳送。它的約定是:
- routing key為一個句點號
.分隔的字符串(我們將被句點號.分隔開的每一段獨立的字符串稱為一個單詞),如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit” - binding key與routing key一樣也是句點號
.分隔的字符串 - binding key中可以存在兩種特殊字符
*與#,用于做模糊匹配,其中*用于匹配一個單詞,#用于匹配多個單詞(可以是零個)

當生產者發(fā)送消息Routing Key=F.C.E的時候,這時候只滿足Queue1,所以會被路由到Queue中,如果Routing Key=A.C.E這時候會被同是路由到Queue1和Queue2中,如果Routing Key=A.F.B時,這里只會發(fā)送一條消息到Queue2中。
headers
headers類型的Exchange不依賴于routing key與binding key的匹配規(guī)則來路由消息,而是根據發(fā)送的消息內容中的headers屬性進行匹配。 在綁定Queue與Exchange時指定一組鍵值對;當消息發(fā)送到Exchange時,RabbitMQ會取到該消息的headers(也是一個鍵值對的形式),對比其中的鍵值對是否完全匹配Queue與Exchange綁定時指定的鍵值對;如果完全匹配則消息會路由到該Queue,否則不會路由到該Queue。 該類型的Exchange沒有用到過(不過也應該很有用武之地),所以不做介紹。
這里在對其進行簡要的表格整理:
小結

核心概念:
Server:又稱Broker ,接受客戶端的連接,實現(xiàn)AMQP實體服務。 安裝rabbitmq-server
Connection:連接,應用程序與Broker的網絡連接 TCP/IP/ 三次握手和四次揮手
Channel:網絡信道,幾乎所有的操作都在Channel中進行,Channel是進行消息讀寫的通道,客戶端可以建立對各Channel,每個Channel代表一個會話任務。
Message :消息:服務與應用程序之間傳送的數(shù)據,由Properties和body組成,Properties可是對消息進行修飾,比如消息的優(yōu)先級,延遲等高級特性,Body則就是消息體的內容。
Virtual Host 虛擬地址,用于進行邏輯隔離,最上層的消息路由,一個虛擬主機理由可以有若干個Exhange和Queueu,同一個虛擬主機里面不能有相同名字的Exchange
Exchange:交換機,接受消息,根據路由鍵發(fā)送消息到綁定的隊列。(==不具備消息存儲的能力==)
Bindings:Exchange和Queue之間的虛擬連接,binding中可以保護多個routing key.
Routing key:是一個路由規(guī)則,虛擬機可以用它來確定如何路由一個特定消息。
Queue:隊列:也成為Message Queue,消息隊列,保存消息并將它們轉發(fā)給消費者。
| 類型名稱 | 類型描述 |
|---|---|
| fanout | 把所有發(fā)送到該Exchange的消息路由到所有與它綁定的Queue中 |
| direct | Routing Key==Binding Key |
| topic | 簡稱模糊匹配,*用于匹配一個單詞,#用于匹配多個單詞(可以是零個) |
| headers | Exchange不依賴于routing key與binding key的匹配規(guī)則來路由消息,而是根據發(fā)送的消息內容中的headers屬性進行匹配。 |
消息發(fā)送與接收原理
消息發(fā)送與接收的整體流程如下圖所示,后面會再分別針對發(fā)送和接收展開介紹:

消息發(fā)布原理
如下圖所示,生產者將消息投遞給exchange,exchange與queue之間已經事先建立好了某個路由規(guī)則binding,exchange根據routingKey和事先建立好的binding進行匹配,決定如何將消息分發(fā)給對應的queue。

消息消費原理
消費者訂閱指定queue,當消息發(fā)送到queue之后,由于RabbitMQ消息消費采用的是push模型,queue會立即將消息發(fā)送給訂閱了該queue的消費者。
- 如果多個訂閱者訂閱同一個queue,則消息只會發(fā)給其中一個訂閱者
- 生產者發(fā)送一條消息,希望多個訂閱者可以消費(消息廣播的場景),多個訂閱者就每個人分別訂閱一個隊列,其中每個隊列和exchange建立相同的binding。

要特別注意的是,這種方案實現(xiàn)的消息廣播是有瓶頸限制的。因為RabbitMQ進行隊列廣播時,是需要在內存中復制消息的,如果廣播的量不大,10、20個這樣的,對性能影響不大;但如果是大量的廣播,則會急劇降低RabbitMQ的性能。因此MQ并不適合對端推送,相比較而言,更適用于服務間推送。
補充說明
ConnectionFactory、Connection、Channel都是RabbitMQ對外提供的API中最基本的對象。Connection是RabbitMQ的socket鏈接,它封裝了socket協(xié)議相關部分邏輯。ConnectionFactory為Connection的制造工廠。Channel是我們與RabbitMQ打交道的最重要的一個接口,我們大部分的業(yè)務操作是在Channel這個接口中完成的,包括定義Queue、定義Exchange、綁定Queue與Exchange、發(fā)布消息等。
Connection就是建立一個TCP連接,生產者和消費者的都是通過TCP的連接到RabbitMQ Server中的,這個后續(xù)會再程序中體現(xiàn)出來。
Channel虛擬連接,建立在上面TCP連接的基礎上,數(shù)據流動都是通過Channel來進行的。為什么不是直接建立在TCP的基礎上進行數(shù)據流動呢?如果建立在TCP的基礎上進行數(shù)據流動,建立和關閉TCP連接有代價。頻繁的建立關閉TCP連接對于系統(tǒng)的性能有很大的影響,而且TCP的連接數(shù)也有限制,這也限制了系統(tǒng)處理高并發(fā)的能力。但是,在TCP連接中建立Channel是沒有上述代價的。