一、背景
RabbitMQ是一個(gè)由erlang開(kāi)發(fā)的AMQP(Advanced Message Queue )的開(kāi)源實(shí)現(xiàn)。AMQP 的出現(xiàn)其實(shí)也是應(yīng)了廣大人民群眾的需求,雖然在同步消息通訊的世界里有很多公開(kāi)標(biāo)準(zhǔn)(如 COBAR的 IIOP ,或者是 SOAP 等),但是在異步消息處理中卻不是這樣,只有大企業(yè)有一些商業(yè)實(shí)現(xiàn)(如微軟的 MSMQ ,IBM 的 Websphere MQ 等),因此,在 2006 年的 6 月,Cisco 、Redhat、iMatix 等聯(lián)合制定了 AMQP 的公開(kāi)標(biāo)準(zhǔn)。
RabbitMQ是由RabbitMQ Technologies Ltd開(kāi)發(fā)并且提供商業(yè)支持的。該公司在2010年4月被SpringSource(VMWare的一個(gè)部門(mén))收購(gòu)。在2013年5月被并入Pivotal。其實(shí)VMWare,Pivotal和EMC本質(zhì)上是一家的。不同的是VMWare是獨(dú)立上市子公司,而Pivotal是整合了EMC的某些資源,現(xiàn)在并沒(méi)有上市。
RabbitMQ的官網(wǎng)是http://www.rabbitmq.com
花絮:本篇文章是一個(gè)系列的文章,本片是開(kāi)篇,后續(xù)會(huì)陸陸續(xù)續(xù)的整理出來(lái),我會(huì)現(xiàn)在我自己個(gè)人博客發(fā)表出來(lái)(www.battleheart.cn),因?yàn)樵谧约旱牟┛屠锩婵梢韵刃薷牧送晟朴行┎粚?duì)的地方,等完善后再發(fā)到博客園,免得誤導(dǎo)大家。
二、基礎(chǔ)概念
講解基礎(chǔ)概念的前面,我們先來(lái)整體構(gòu)造一個(gè)結(jié)構(gòu)圖,這樣會(huì)方便們更好地去理解RabbitMQ的基本原理。

通過(guò)上面這張結(jié)構(gòu)圖既能夠清晰的看清楚整體的send Message到Receive Message的一個(gè)大致的流程。當(dāng)然上面有很多名詞都相比還沒(méi)有介紹到,不要著急接下來(lái)我們就開(kāi)始對(duì)其進(jìn)行詳細(xì)的講解。
Queue
Queue(隊(duì)列)RabbitMQ的作用是存儲(chǔ)消息,隊(duì)列的特性是先進(jìn)先出。上圖可以清晰地看到Client A和Client B是生產(chǎn)者,生產(chǎn)者生產(chǎn)消息最終被送到RabbitMQ的內(nèi)部對(duì)象Queue中去,而消費(fèi)者則是從Queue隊(duì)列中取出數(shù)據(jù)??梢院?jiǎn)化成表示為:

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

這里的prefetchCount=1是指每次從Queue中發(fā)送一條消息來(lái)。等消費(fèi)者處理完這條消息后Queue會(huì)再發(fā)送一條消息給消費(fèi)者。
Exchange
我們?cè)陂_(kāi)篇的時(shí)候就留了一個(gè)坑,就是那個(gè)應(yīng)用結(jié)構(gòu)圖里面,消費(fèi)者Client A和消費(fèi)者Client B是如何知道我發(fā)送的消息是給Queue1還是給Queue2,有沒(méi)有過(guò)這個(gè)問(wèn)題,那么我們就來(lái)解開(kāi)這個(gè)面紗,看看到底是個(gè)什么構(gòu)造。首先明確一點(diǎn)就是生產(chǎn)者產(chǎn)生的消息并不是直接發(fā)送給消息隊(duì)列Queue的,而是要經(jīng)過(guò)Exchange(交換器),由Exchange再將消息路由到一個(gè)或多個(gè)Queue,當(dāng)然這里還會(huì)對(duì)不符合路由規(guī)則的消息進(jìn)行丟棄掉,這里指的是后續(xù)要談到的Exchange Type。那么Exchange是怎樣將消息準(zhǔn)確的推送到對(duì)應(yīng)的Queue的呢?那么這里的功勞最大的當(dāng)屬Binding,RabbitMQ是通過(guò)Binding將Exchange和Queue鏈接在一起,這樣Exchange就知道如何將消息準(zhǔn)確的推送到Queue中去。簡(jiǎn)單示意圖如下所示:

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

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

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

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

補(bǔ)充說(shuō)明:
ConnectionFactory、Connection、Channel
ConnectionFactory、Connection、Channel都是RabbitMQ對(duì)外提供的API中最基本的對(duì)象。Connection是RabbitMQ的socket鏈接,它封裝了socket協(xié)議相關(guān)部分邏輯。ConnectionFactory為Connection的制造工廠。
Channel是我們與RabbitMQ打交道的最重要的一個(gè)接口,我們大部分的業(yè)務(wù)操作是在Channel這個(gè)接口中完成的,包括定義Queue、定義Exchange、綁定Queue與Exchange、發(fā)布消息等。
Connection就是建立一個(gè)TCP連接,生產(chǎn)者和消費(fèi)者的都是通過(guò)TCP的連接到RabbitMQ Server中的,這個(gè)后續(xù)會(huì)再程序中體現(xiàn)出來(lái)。
Channel虛擬連接,建立在上面TCP連接的基礎(chǔ)上,數(shù)據(jù)流動(dòng)都是通過(guò)Channel來(lái)進(jìn)行的。為什么不是直接建立在TCP的基礎(chǔ)上進(jìn)行數(shù)據(jù)流動(dòng)呢?如果建立在TCP的基礎(chǔ)上進(jìn)行數(shù)據(jù)流動(dòng),建立和關(guān)閉TCP連接有代價(jià)。頻繁的建立關(guān)閉TCP連接對(duì)于系統(tǒng)的性能有很大的影響,而且TCP的連接數(shù)也有限制,這也限制了系統(tǒng)處理高并發(fā)的能力。但是,在TCP連接中建立Channel是沒(méi)有上述代價(jià)的。