1. 歷史
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
2. 應(yīng)用場(chǎng)景
言歸正傳鸵隧。RabbitMQ,或者說(shuō)AMQP解決了什么問(wèn)題财破,或者說(shuō)它的應(yīng)用場(chǎng)景是什么掰派?
對(duì)于一個(gè)大型的軟件系統(tǒng)來(lái)說(shuō),它會(huì)有很多的組件或者說(shuō)模塊或者說(shuō)子系統(tǒng)或者(subsystem or Component or submodule)左痢。那么這些模塊的如何通信靡羡?這和傳統(tǒng)的IPC有很大的區(qū)別。傳統(tǒng)的IPC很多都是在單一系統(tǒng)上的俊性,模塊耦合性很大略步,不適合擴(kuò)展(Scalability);如果使用socket那么不同的模塊的確可以部署到不同的機(jī)器上定页,但是還是有很多問(wèn)題需要解決趟薄。比如:
- 1)信息的發(fā)送者和接收者如何維持這個(gè)連接,如果一方的連接中斷典徊,這期間的數(shù)據(jù)如何方式丟失杭煎?
- 2)如何降低發(fā)送者和接收者的耦合度?
- 3)如何讓Priority高的接收者先接到數(shù)據(jù)卒落?
- 4)如何做到load balance羡铲?有效均衡接收者的負(fù)載?
- 5)如何有效的將數(shù)據(jù)發(fā)送到相關(guān)的接收者儡毕?也就是說(shuō)將接收者subscribe 不同的數(shù)據(jù)也切,如何做有效的filter。
- 6)如何做到可擴(kuò)展腰湾,甚至將這個(gè)通信模塊發(fā)到cluster上雷恃?
- 7)如何保證接收者接收到了完整,正確的數(shù)據(jù)费坊?
AMDQ協(xié)議解決了以上的問(wèn)題倒槐,而RabbitMQ實(shí)現(xiàn)了AMQP。
3. 系統(tǒng)架構(gòu)
RabbitMQ Server: 也叫broker server葵萎,它不是運(yùn)送食物的卡車(chē)导犹,而是一種傳輸服務(wù)唱凯。原話(huà)是RabbitMQisn’t a food truck, it’s a delivery service. 他的角色就是維護(hù)一條從Producer到Consumer的路線(xiàn)羡忘,保證數(shù)據(jù)能夠按照指定的方式進(jìn)行傳輸谎痢。但是這個(gè)保證也不是100%的保證,但是對(duì)于普通的應(yīng)用來(lái)說(shuō)這已經(jīng)足夠了卷雕。當(dāng)然對(duì)于商業(yè)系統(tǒng)來(lái)說(shuō)节猿,可以再做一層數(shù)據(jù)一致性的guard,就可以徹底保證系統(tǒng)的一致性了漫雕。
Client P: 也叫Producer滨嘱,數(shù)據(jù)的發(fā)送方。createmessages and publish (send) them to a broker server (RabbitMQ).一個(gè)Message有兩個(gè)部分:payload(有效載荷)和label(標(biāo)簽)浸间。payload顧名思義就是傳輸?shù)臄?shù)據(jù)太雨。label是exchange的名字或者說(shuō)是一個(gè)tag,它描述了payload魁蒜,而且RabbitMQ也是通過(guò)這個(gè)label來(lái)決定把這個(gè)Message發(fā)給哪個(gè)Consumer囊扳。AMQP僅僅描述了label,而RabbitMQ決定了如何使用這個(gè)label的規(guī)則兜看。
Client C: 也叫Consumer锥咸,數(shù)據(jù)的接收方。Consumersattach to a broker server (RabbitMQ) and subscribe to a queue细移。把queue比作是一個(gè)有名字的郵箱搏予。當(dāng)有Message到達(dá)某個(gè)郵箱后,RabbitMQ把它發(fā)送給它的某個(gè)訂閱者即Consumer弧轧。當(dāng)然可能會(huì)把同一個(gè)Message發(fā)送給很多的Consumer雪侥。在這個(gè)Message中,只有payload精绎,label已經(jīng)被刪掉了速缨。對(duì)于Consumer來(lái)說(shuō),它是不知道誰(shuí)發(fā)送的這個(gè)信息的捺典。就是協(xié)議本身不支持鸟廓。但是當(dāng)然了如果Producer發(fā)送的payload包含了Producer的信息就另當(dāng)別論了。
對(duì)于一個(gè)數(shù)據(jù)從Producer到Consumer的正確傳遞襟己,還有三個(gè)概念需要明確:exchanges, queues and bindings引谜。
Exchanges are where producers publish their messages.
Queues are where the messages end up and are received by consumers
Bindings are how the messages get routed from the exchange to particular queues.
還有幾個(gè)概念是上述圖中沒(méi)有標(biāo)明的,那就是Connection(連接)擎浴,Channel(通道员咽,頻道)。
Connection: 就是一個(gè)TCP的連接贮预。Producer和Consumer都是通過(guò)TCP連接到RabbitMQ Server的贝室。以后我們可以看到契讲,程序的起始處就是建立這個(gè)TCP連接。
Channels: 虛擬連接滑频。它建立在上述的TCP連接中捡偏。數(shù)據(jù)流動(dòng)都是在Channel中進(jìn)行的。也就是說(shuō)峡迷,一般情況是程序起始建立TCP連接银伟,第二步就是建立這個(gè)Channel。
那么绘搞,為什么使用Channel彤避,而不是直接使用TCP連接?
對(duì)于OS來(lái)說(shuō)夯辖,建立和關(guān)閉TCP連接是有代價(jià)的琉预,頻繁的建立關(guān)閉TCP連接對(duì)于系統(tǒng)的性能有很大的影響,而且TCP的連接數(shù)也有限制蒿褂,這也限制了系統(tǒng)處理高并發(fā)的能力圆米。但是,在TCP連接中建立Channel是沒(méi)有上述代價(jià)的贮缅。對(duì)于Producer或者Consumer來(lái)說(shuō)榨咐,可以并發(fā)的使用多個(gè)Channel進(jìn)行Publish或者Receive。有實(shí)驗(yàn)表明谴供,1s的數(shù)據(jù)可以Publish10K的數(shù)據(jù)包块茁。當(dāng)然對(duì)于不同的硬件環(huán)境,不同的數(shù)據(jù)包大小這個(gè)數(shù)據(jù)肯定不一樣桂肌,但是我只想說(shuō)明数焊,對(duì)于普通的Consumer或者Producer來(lái)說(shuō),這已經(jīng)足夠了崎场。如果不夠用佩耳,你考慮的應(yīng)該是如何細(xì)化split你的設(shè)計(jì)。
Broker: 簡(jiǎn)單來(lái)說(shuō)就是消息隊(duì)列服務(wù)器實(shí)體谭跨。
Exchange: 消息交換機(jī)干厚,它指定消息按什么規(guī)則,路由到哪個(gè)隊(duì)列螃宙。
Queue: 消息隊(duì)列載體蛮瞄,每個(gè)消息都會(huì)被投入到一個(gè)或多個(gè)隊(duì)列。
Binding: 綁定谆扎,它的作用就是把exchange和queue按照路由規(guī)則綁定起來(lái)挂捅。
Routing Key: 路由關(guān)鍵字,exchange根據(jù)這個(gè)關(guān)鍵字進(jìn)行消息投遞堂湖。
vhost: 虛擬主機(jī)闲先,一個(gè)broker里可以開(kāi)設(shè)多個(gè)vhost状土,用作不同用戶(hù)的權(quán)限分離。
producer: 消息生產(chǎn)者伺糠,就是投遞消息的程序蒙谓。
consumer: 消息消費(fèi)者,就是接受消息的程序。
channel: 消息通道,在客戶(hù)端的每個(gè)連接里绵脯,可建立多個(gè)channel拧揽,每個(gè)channel代表一個(gè)會(huì)話(huà)任務(wù)。
由Exchange灶挟,Queue琉朽,RoutingKey三個(gè)才能決定一個(gè)從Exchange到Queue的唯一的線(xiàn)路。
4.基本概念
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ā)布消息等寞宫。
Queue
Queue(隊(duì)列)是RabbitMQ的內(nèi)部對(duì)象,用于存儲(chǔ)消息拉鹃,用下圖表示辈赋。
queue
RabbitMQ中的消息都只能存儲(chǔ)在Queue中,生產(chǎn)者(下圖中的P)生產(chǎn)消息并最終投遞到Queue中膏燕,消費(fèi)者(下圖中的C)可以從Queue中獲取消息并消費(fèi)钥屈。
多個(gè)消費(fèi)者可以訂閱同一個(gè)Queue,這時(shí)Queue中的消息會(huì)被平均分?jǐn)偨o多個(gè)消費(fèi)者進(jìn)行處理煌寇,而不是每個(gè)消費(fèi)者都收到所有的消息并處理焕蹄。
Message acknowledgment
在實(shí)際應(yīng)用中,可能會(huì)發(fā)生消費(fèi)者收到Queue中的消息阀溶,但沒(méi)有處理完成就宕機(jī)(或出現(xiàn)其他意外)的情況腻脏,這種情況下就可能會(huì)導(dǎo)致消息丟失鸦泳。為了避免這種情況發(fā)生,我們可以要求消費(fèi)者在消費(fèi)完消息后發(fā)送一個(gè)回執(zhí)給RabbitMQ永品,RabbitMQ收到消息回執(zhí)(Message acknowledgment)后才將該消息從Queue中移除做鹰;如果RabbitMQ沒(méi)有收到回執(zhí)并檢測(cè)到消費(fèi)者的RabbitMQ連接斷開(kāi),則RabbitMQ會(huì)將該消息發(fā)送給其他消費(fèi)者(如果存在多個(gè)消費(fèi)者)進(jìn)行處理鼎姐。這里不存在timeout概念钾麸,一個(gè)消費(fèi)者處理消息時(shí)間再長(zhǎng)也不會(huì)導(dǎo)致該消息被發(fā)送給其他消費(fèi)者,除非它的RabbitMQ連接斷開(kāi)炕桨。
這里會(huì)產(chǎn)生另外一個(gè)問(wèn)題饭尝,如果我們的開(kāi)發(fā)人員在處理完業(yè)務(wù)邏輯后,忘記發(fā)送回執(zhí)給RabbitMQ献宫,這將會(huì)導(dǎo)致嚴(yán)重的bug——Queue中堆積的消息會(huì)越來(lái)越多钥平;消費(fèi)者重啟后會(huì)重復(fù)消費(fèi)這些消息并重復(fù)執(zhí)行業(yè)務(wù)邏輯。
另外pub message是沒(méi)有ack的姊途。
Message durability
如果我們希望即使在RabbitMQ服務(wù)重啟的情況下涉瘾,也不會(huì)丟失消息,我們可以將Queue與Message都設(shè)置為可持久化的(durable)捷兰,這樣可以保證絕大部分情況下我們的RabbitMQ消息不會(huì)丟失立叛。但依然解決不了小概率丟失事件的發(fā)生(比如RabbitMQ服務(wù)器已經(jīng)接收到生產(chǎn)者的消息,但還沒(méi)來(lái)得及持久化該消息時(shí)RabbitMQ服務(wù)器就斷電了)贡茅,如果我們需要對(duì)這種小概率事件也要管理起來(lái)秘蛇,那么我們要用到事務(wù)。由于這里僅為RabbitMQ的簡(jiǎn)單介紹友扰,所以這里將不講解RabbitMQ相關(guān)的事務(wù)彤叉。
Prefetch count
前面我們講到如果有多個(gè)消費(fèi)者同時(shí)訂閱同一個(gè)Queue中的消息,Queue中的消息會(huì)被平攤給多個(gè)消費(fèi)者村怪。這時(shí)如果每個(gè)消息的處理時(shí)間不同秽浇,就有可能會(huì)導(dǎo)致某些消費(fèi)者一直在忙,而另外一些消費(fèi)者很快就處理完手頭工作并一直空閑的情況甚负。我們可以通過(guò)設(shè)置prefetchCount來(lái)限制Queue每次發(fā)送給每個(gè)消費(fèi)者的消息數(shù)柬焕,比如我們?cè)O(shè)置prefetchCount=1,則Queue每次給每個(gè)消費(fèi)者發(fā)送一條消息梭域;消費(fèi)者處理完這條消息后Queue會(huì)再給該消費(fèi)者發(fā)送一條消息斑举。
Exchange
在上一節(jié)我們看到生產(chǎn)者將消息投遞到Queue中,實(shí)際上這在RabbitMQ中這種事情永遠(yuǎn)都不會(huì)發(fā)生病涨。實(shí)際的情況是富玷,生產(chǎn)者將消息發(fā)送到Exchange(交換器,下圖中的X),由Exchange將消息路由到一個(gè)或多個(gè)Queue中(或者丟棄)赎懦。
Exchange是按照什么邏輯將消息路由到Queue的雀鹃?這個(gè)將在Binding一節(jié)介紹。
RabbitMQ中的Exchange有四種類(lèi)型励两,不同的類(lèi)型有著不同的路由策略黎茎,這將在Exchange Types一節(jié)介紹。
routing key
生產(chǎn)者在將消息發(fā)送給Exchange的時(shí)候当悔,一般會(huì)指定一個(gè)routing key傅瞻,來(lái)指定這個(gè)消息的路由規(guī)則,而這個(gè)routing key需要與Exchange Type及binding key聯(lián)合使用才能最終生效盲憎。
在Exchange Type與binding key固定的情況下(在正常使用時(shí)一般這些內(nèi)容都是固定配置好的)嗅骄,我們的生產(chǎn)者就可以在發(fā)送消息給Exchange時(shí),通過(guò)指定routing key來(lái)決定消息流向哪里焙畔。
RabbitMQ為routing key設(shè)定的長(zhǎng)度限制為255 bytes掸读。
Binding
RabbitMQ中通過(guò)Binding將Exchange與Queue關(guān)聯(lián)起來(lái),這樣RabbitMQ就知道如何正確地將消息路由到指定的Queue了宏多。
Binding key
在綁定(Binding)Exchange與Queue的同時(shí),一般會(huì)指定一個(gè)binding key澡罚;消費(fèi)者將消息發(fā)送給Exchange時(shí)伸但,一般會(huì)指定一個(gè)routing key;當(dāng)binding key與routing key相匹配時(shí)留搔,消息將會(huì)被路由到對(duì)應(yīng)的Queue中更胖。這個(gè)將在Exchange Types章節(jié)會(huì)列舉實(shí)際的例子加以說(shuō)明。
在綁定多個(gè)Queue到同一個(gè)Exchange的時(shí)候隔显,這些Binding允許使用相同的binding key却妨。
binding key 并不是在所有情況下都生效,它依賴(lài)于Exchange Type括眠,比如fanout類(lèi)型的Exchange就會(huì)無(wú)視binding key彪标,而是將消息路由到所有綁定到該Exchange的Queue。
Exchange Types
RabbitMQ常用的Exchange Type有fanout掷豺、direct捞烟、topic、headers這四種(AMQP規(guī)范里還提到兩種Exchange Type当船,分別為system與自定義题画,這里不予以描述),下面分別進(jìn)行介紹德频。
fanout
fanout類(lèi)型的Exchange路由規(guī)則非常簡(jiǎn)單苍息,它會(huì)把所有發(fā)送到該Exchange的消息路由到所有與它綁定的Queue中。
上圖中,生產(chǎn)者(P)發(fā)送到Exchange(X)的所有消息都會(huì)路由到圖中的兩個(gè)Queue竞思,并最終被兩個(gè)消費(fèi)者(C1與C2)消費(fèi)桌粉。
direct
direct類(lèi)型的Exchange路由規(guī)則也很簡(jiǎn)單,它會(huì)把消息路由到那些binding key與routing key完全匹配的Queue中衙四。
以上圖的配置為例铃肯,我們以routingKey=”error”發(fā)送消息到Exchange,則消息會(huì)路由到Queue1(amqp.gen-S9b…传蹈,這是由RabbitMQ自動(dòng)生成的Queue名稱(chēng))和Queue2(amqp.gen-Agl…)押逼;如果我們以routingKey=”info”或routingKey=”warning”來(lái)發(fā)送消息,則消息只會(huì)路由到Queue2惦界。如果我們以其他routingKey發(fā)送消息挑格,則消息不會(huì)路由到這兩個(gè)Queue中。
topic
前面講到direct類(lèi)型的Exchange路由規(guī)則是完全匹配binding key與routing key沾歪,但這種嚴(yán)格的匹配方式在很多情況下不能滿(mǎn)足實(shí)際業(yè)務(wù)需求漂彤。topic類(lèi)型的Exchange在匹配規(guī)則上進(jìn)行了擴(kuò)展,它與direct類(lèi)型的Exchage相似灾搏,也是將消息路由到binding key與routing key相匹配的Queue中挫望,但這里的匹配規(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è))
以上圖中的配置為例丛晦,routingKey=”quick.orange.rabbit”的消息會(huì)同時(shí)路由到Q1與Q2奕纫,routingKey=”lazy.orange.fox”的消息會(huì)路由到Q1,routingKey=”lazy.brown.fox”的消息會(huì)路由到Q2烫沙,routingKey=”lazy.pink.rabbit”的消息會(huì)路由到Q2(只會(huì)投遞給Q2一次匹层,雖然這個(gè)routingKey與Q2的兩個(gè)bindingKey都匹配);routingKey=”quick.brown.fox”斧吐、routingKey=”orange”又固、routingKey=”quick.orange.male.rabbit”的消息將會(huì)被丟棄,因?yàn)樗鼈儧](méi)有匹配任何bindingKey煤率。
headers
headers類(lèi)型的Exchange不依賴(lài)于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)該很有用武之地),所以不做介紹担锤。
RPC
MQ本身是基于異步的消息處理蔚晨,前面的示例中所有的生產(chǎn)者(P)將消息發(fā)送到RabbitMQ后不會(huì)知道消費(fèi)者(C)處理成功或者失敗(甚至連有沒(méi)有消費(fèi)者來(lái)處理這條消息都不知道)肛循。
但實(shí)際的應(yīng)用場(chǎng)景中铭腕,我們很可能需要一些同步處理,需要同步等待服務(wù)端將我的消息處理完成后再進(jìn)行下一步處理多糠。這相當(dāng)于RPC(Remote Procedure Call累舷,遠(yuǎn)程過(guò)程調(diào)用)。
在RabbitMQ中也支持RPC夹孔。
RabbitMQ中實(shí)現(xiàn)RPC的機(jī)制是:
- 客戶(hù)端發(fā)送請(qǐng)求(消息)時(shí)被盈,在消息的屬性(MessageProperties,在AMQP協(xié)議中定義了14中properties搭伤,這些屬性會(huì)隨著消息一起發(fā)送)中設(shè)置兩個(gè)值replyTo(一個(gè)Queue名稱(chēng)只怎,用于告訴服務(wù)器處理完成后將通知我的消息發(fā)送到這個(gè)Queue中)和correlationId(此次請(qǐng)求的標(biāo)識(shí)號(hào),服務(wù)器處理完成后需要將此屬性返還闷畸,客戶(hù)端將根據(jù)這個(gè)id了解哪條請(qǐng)求被成功執(zhí)行了或執(zhí)行失敵⑴巍)
- 服務(wù)器端收到消息并處理
- 服務(wù)器端處理完消息后,將生成一條應(yīng)答消息到replyTo指定的Queue佑菩,同時(shí)帶上correlationId屬性
- 客戶(hù)端之前已訂閱replyTo指定的Queue,從中收到服務(wù)器的應(yīng)答消息后裁赠,根據(jù)其中的correlationId屬性分析哪條請(qǐng)求被執(zhí)行了殿漠,根據(jù)執(zhí)行結(jié)果進(jìn)行后續(xù)業(yè)務(wù)處理
5. 細(xì)節(jié)闡明
使用ack確認(rèn)Message的正確傳遞
默認(rèn)情況下,如果Message 已經(jīng)被某個(gè)Consumer正確的接收到了佩捞,那么該Message就會(huì)被從queue中移除绞幌。當(dāng)然也可以讓同一個(gè)Message發(fā)送到很多的Consumer。
如果一個(gè)queue沒(méi)被任何的Consumer Subscribe(訂閱)一忱,那么莲蜘,如果這個(gè)queue有數(shù)據(jù)到達(dá),那么這個(gè)數(shù)據(jù)會(huì)被cache帘营,不會(huì)被丟棄票渠。當(dāng)有Consumer時(shí),這個(gè)數(shù)據(jù)會(huì)被立即發(fā)送到這個(gè)Consumer芬迄,這個(gè)數(shù)據(jù)被Consumer正確收到時(shí)问顷,這個(gè)數(shù)據(jù)就被從queue中刪除。
那么什么是正確收到呢?通過(guò)ack杜窄。每個(gè)Message都要被acknowledged(確認(rèn)肠骆,ack)。我們可以顯示的在程序中去ack塞耕,也可以自動(dòng)的ack蚀腿。如果有數(shù)據(jù)沒(méi)有被ack,那么RabbitMQ Server會(huì)把這個(gè)信息發(fā)送到下一個(gè)Consumer扫外。
如果這個(gè)app有bug莉钙,忘記了ack,那么RabbitMQ Server不會(huì)再發(fā)送數(shù)據(jù)給它畏浆,因?yàn)镾erver認(rèn)為這個(gè)Consumer處理能力有限胆胰。
而且ack的機(jī)制可以起到限流的作用(Benefitto throttling):在Consumer處理完成數(shù)據(jù)后發(fā)送ack,甚至在額外的延時(shí)后發(fā)送ack刻获,將有效的balance Consumer的load蜀涨。
當(dāng)然對(duì)于實(shí)際的例子,比如我們可能會(huì)對(duì)某些數(shù)據(jù)進(jìn)行merge蝎毡,比如merge 4s內(nèi)的數(shù)據(jù)厚柳,然后sleep 4s后再獲取數(shù)據(jù)。特別是在監(jiān)聽(tīng)系統(tǒng)的state沐兵,我們不希望所有的state實(shí)時(shí)的傳遞上去别垮,而是希望有一定的延時(shí)。這樣可以減少某些IO扎谎,而且終端用戶(hù)也不會(huì)感覺(jué)到碳想。
Reject a message
有兩種方式,第一種的Reject可以讓RabbitMQ Server將該Message 發(fā)送到下一個(gè)Consumer毁靶。第二種是從queue中立即刪除該Message胧奔。
Creating a queue
Consumer和Procuder都可以通過(guò) queue.declare 創(chuàng)建queue。對(duì)于某個(gè)Channel來(lái)說(shuō)预吆,Consumer不能declare一個(gè)queue龙填,卻訂閱其他的queue。當(dāng)然也可以創(chuàng)建私有的queue拐叉。這樣只有app本身才可以使用這個(gè)queue岩遗。queue也可以自動(dòng)刪除,被標(biāo)為auto-delete的queue在最后一個(gè)Consumer unsubscribe后就會(huì)被自動(dòng)刪除凤瘦。那么如果是創(chuàng)建一個(gè)已經(jīng)存在的queue呢宿礁?那么不會(huì)有任何的影響。需要注意的是沒(méi)有任何的影響廷粒,也就是說(shuō)第二次創(chuàng)建如果參數(shù)和第一次不一樣窘拯,那么該操作雖然成功红且,但是queue的屬性并不會(huì)被修改。
那么誰(shuí)應(yīng)該負(fù)責(zé)創(chuàng)建這個(gè)queue呢涤姊?是Consumer暇番,還是Producer?
如果queue不存在思喊,當(dāng)然Consumer不會(huì)得到任何的Message壁酬。但是如果queue不存在,那么Producer Publish的Message會(huì)被丟棄恨课。所以舆乔,還是為了數(shù)據(jù)不丟失,Consumer和Producer都try to create the queue剂公!反正不管怎么樣希俩,這個(gè)接口都不會(huì)出問(wèn)題。
queue對(duì)load balance的處理是完美的纲辽。對(duì)于多個(gè)Consumer來(lái)說(shuō)颜武,RabbitMQ 使用循環(huán)的方式(round-robin)的方式均衡的發(fā)送給不同的Consumer。
Exchanges
從架構(gòu)圖可以看出拖吼,Procuder Publish的Message進(jìn)入了Exchange鳞上。接著通過(guò)“routing keys”, RabbitMQ會(huì)找到應(yīng)該把這個(gè)Message放到哪個(gè)queue里吊档。queue也是通過(guò)這個(gè)routing keys來(lái)做的綁定篙议。
有三種類(lèi)型的Exchanges:direct, fanout,topic。 每個(gè)實(shí)現(xiàn)了不同的路由算法(routing algorithm)怠硼。
- Direct exchange: 如果 routing key 匹配, 那么Message就會(huì)被傳遞到相應(yīng)的queue中鬼贱。其實(shí)在queue創(chuàng)建時(shí),它會(huì)自動(dòng)的以queue的名字作為routing key來(lái)綁定那個(gè)exchange香璃。
- Fanout exchange: 會(huì)向響應(yīng)的queue廣播吩愧。
- Topic exchange:對(duì)key進(jìn)行模式匹配,比如ab可以傳遞到所有ab的queue增显。
Virtual hosts
每個(gè)virtual host本質(zhì)上都是一個(gè)RabbitMQ Server,擁有它自己的queue脐帝,exchagne同云,和bings rule等等。這保證了你可以在多個(gè)不同的application中使用RabbitMQ堵腹。
個(gè)人介紹:
高廣超 :多年一線(xiàn)互聯(lián)網(wǎng)研發(fā)與架構(gòu)設(shè)計(jì)經(jīng)驗(yàn)炸站,擅長(zhǎng)設(shè)計(jì)與落地高可用、高性能互聯(lián)網(wǎng)架構(gòu)疚顷。目前就職于美團(tuán)網(wǎng)旱易,負(fù)責(zé)核心業(yè)務(wù)研發(fā)工作禁偎。