和RocketMQ一樣堡赔,RabbitMQ作為消息中間件锰茉,主要負(fù)責(zé)接收倔既、存儲和轉(zhuǎn)發(fā)消息恕曲。同時RabbitMQ也有生產(chǎn)者和消費者的概念,我們先來看看其整體模型架構(gòu)渤涌。如下圖:
1. 核心概念介紹
1.1 生產(chǎn)者和消費者
- Producer:生產(chǎn)者佩谣,投遞消息的一方。
通常來說实蓬,一條消息可以包含2個部分:消息體和標(biāo)簽茸俭。消息體一般是一個帶有業(yè)務(wù)邏輯結(jié)構(gòu)的數(shù)據(jù),而標(biāo)簽則用來描述這條消息瞳秽,比如一個交換機(jī)的名稱和一個路由鍵瓣履。
- Consumer:消費者率翅,接收消息的一方练俐。
消費者連接到RabbitMQ服務(wù)器,并訂閱到隊列上冕臭。當(dāng)消費者消費一條消息時腺晾,只是消費消息的消息體,在消息路由的過程中辜贵,消息的標(biāo)簽會丟失悯蝉,存入到隊列中的消息只有消息體,實際上托慨,消費者也不需要關(guān)心消息的生產(chǎn)者是誰鼻由。
- Broker:消息中間件所在的服務(wù)節(jié)點。
消息流轉(zhuǎn)過程如下:
1.2 隊列
- Queue:隊列厚棵,是RabbitMQ的內(nèi)部對象蕉世,用于存儲消息。
RabbitMQ中消息都只能存儲在隊列中婆硬,這一點和RocketMQ不同狠轻,RocketMQ將消息存儲在topic這個邏輯層面,而相對應(yīng)的隊列邏輯只是topic實際存儲文件中的位移標(biāo)識彬犯。
多個消費者可以訂閱同一隊列向楼,這時隊列中的消息會被平均分?jǐn)偨o多個消費者進(jìn)行處理,而不是每個消費者都收到所有的消息并處理谐区。(類似RocketMQ中的負(fù)載均衡模式)
RabbitMQ不支持隊列層面
的廣播消費(但并不是說RabbitMQ不支持廣播消費)湖蜕,RocketMQ是支持廣播消費的。
1.3 交換器宋列、路由鍵昭抒、綁定
- Exchange:交換器,實際上消息生產(chǎn)者并不是直接將消息發(fā)送到隊列的。而是將消息發(fā)送給交換器戈鲁,由交換器將消息路由到一個或多個隊列中仇参。如果路由不到,或許會返回給生產(chǎn)者婆殿,或許直接丟棄诈乒。
其實從大的層面上來說芜繁,和RocketMQ消息投遞的設(shè)計理念是一樣的桩卵。在RocketMQ中,生產(chǎn)者也并不是直接往Broker投遞消息的置尔,而是通過NameServer和Topic共同決定投遞到某個Broker中消约。
- RoutingKey:路由鍵肠鲫,生產(chǎn)者將消息發(fā)給交換器的時候,一般會指定一個RoutingKey或粮,用來指定這個消息的路由規(guī)則导饲,而這個RoutingKey需要與交換器類型和綁定鍵(BindingKey)聯(lián)合使用才能最終生效。
- Binding:綁定氯材,RabbitMQ中通過綁定將交換器與隊列關(guān)聯(lián)起來渣锦,在綁定的時候一般會指定一個綁定鍵(BindingKey),這樣RabbitMQ就知道如何正確地將消息路由到隊列中了氢哮。
這里可能對路由鍵和綁定鍵存在一定的理解混淆袋毙,其實很簡單,綁定鍵就是一個連接交換器和隊列的關(guān)鍵詞冗尤,而路由鍵是交換器來根據(jù)自身類型以及綁定關(guān)系找到適配隊列的關(guān)鍵詞听盖。
雖然RocketMQ中沒有這樣的概念,但是RocketMQ中也有類似的設(shè)計理念裂七。生產(chǎn)者在發(fā)送一條消息時皆看,需要指定Topic,還可以指定Tags碍讯。訂閱了同一個Topic的消費者悬蔽,還可以根據(jù)Tags來判斷是否消費消息。也即間接的消費某一類消息捉兴,而不是消費這個Topic下所有的消息蝎困。
1.4 交換器類型
常用的交換器類型有fanout
、direct
倍啥、topic
這三種禾乘。
- fanout
這種類型的交換器,它會把生產(chǎn)者發(fā)送的消息路由到所有與該交換器綁定的隊列中虽缕。也就是說始藕,此時交換器不會去理會RountingKey
是什么了。
- direct
這種類型的交換器,它會把生產(chǎn)者發(fā)送的消息路由到那些BindingKey
和RoutingKey
完全匹配的隊列中伍派。
- topic
fanout
類型的交換器太過于隨便江耀,direct
類型的交換器又太過于嚴(yán)格。而topic
類型的交換器正是介于fanout
和direct
之間诉植,和direct
類型的交換器類似祥国,也是講消息路由到BindingKey
和RoutingKey
相匹配的隊列中,但這里的匹配規(guī)則有些不同晾腔,它允許BindingKey
中存在兩種特殊字符串*
和#
用于做模糊匹配舌稀,其中*
用來匹配一個單詞,#
用來匹配0個或多個單詞灼擂。舉例:
1)壁查、路由鍵為"com.rabbitmq.client"的消息會同時路由到Queue1和Queue2中。
2)剔应、路由鍵為"com.hidden.client"的消息只會路由到Queue2中睡腿。
3)、路由鍵為"com.hidden.demo"的消息只會路由到Queue2中领斥。
4)嫉到、路由鍵為"java.rabbitmq.demo"的消息只會路由到Queue1中沃暗。
5)月洛、路由鍵為"java.util.concurrent"的消息將會被丟棄或者返回給生產(chǎn)者。
2. RabbitMQ運(yùn)轉(zhuǎn)流程
了解以上的RabbitMQ架構(gòu)模式以及相關(guān)概念孽锥,再結(jié)合《RabbitMQ:安裝》中的例子回顧一下整個消息隊列的使用過程嚼黔。
-
生產(chǎn)者發(fā)送消息
- 生產(chǎn)者通過
ConnectionFactory
連接到RabbitMQ Broker(RocketMQ中,生產(chǎn)者和消費者是跟NameServer進(jìn)行連接的)惜辑,建立一個連接Connection
唬涧,開啟一個信道Channel
。 - 生產(chǎn)者聲明一個交換器并設(shè)置相關(guān)屬性盛撑,比如交換器類型碎节、是否持久化等。
- 生產(chǎn)者聲明一個隊列并設(shè)置相關(guān)屬性抵卫,比如是否排他狮荔、是否持久化、是否自動刪除等介粘。
- 生產(chǎn)者通過路由鍵將交換器和隊列進(jìn)行綁定殖氏。
- 生產(chǎn)者發(fā)送消息至RabbitMQ Broker,其中包含路由鍵姻采、交換器等信息雅采。
- 相應(yīng)的交換器根據(jù)接收到的路由鍵查看相匹配的隊列。
- 如果找到,則將消息存入相應(yīng)的隊列中婚瓜;否則根據(jù)生產(chǎn)者配置的屬性選擇丟棄還是回退給生產(chǎn)者宝鼓。
- 關(guān)閉信道、關(guān)閉連接巴刻。
實際上席函,在生產(chǎn)環(huán)境,交換器冈涧、隊列茂附、綁定關(guān)系是提前創(chuàng)建好的。不需要在生產(chǎn)者發(fā)送消息時顯示的聲明交換器督弓、隊列和綁定關(guān)系营曼。
- 生產(chǎn)者通過
- 消費者消費消息
- 消費者連接到RabbitMQ Broker,建立一個連接愚隧,開啟一個信道蒂阱。
- 消費者向RabbitMQ Broker請求消費相應(yīng)隊列中的消息。
- 等到RabbitMQ Broker回應(yīng)并投遞相應(yīng)隊列中的消費狂塘,消費者接收消息录煤。
- 消費者確認(rèn)接收到的消息。
- RabbitMQ從隊列中刪除相應(yīng)已經(jīng)被確認(rèn)的消息荞胡。
- 關(guān)閉信息妈踊、關(guān)閉連接。
在這里泪漂,我們知道無論是生產(chǎn)者還是消費者廊营,都需要和RabbitMQ Broker建立連接,這個連接就是一條TCP連接萝勤,也就是Connection露筒。一旦TCP連接建立起來,客戶端緊接著可以創(chuàng)建一個信道Channel敌卓,每個信道都會被指派一個唯一的ID慎式。信道是建立在Connection之上的虛擬連接,RabbitMQ處理的每條指令都是通過信道完成的趟径。
我們完全可以直接使用Connection就能完成信道的工作瘪吏,為什么還要引入信道呢?試想這樣的一個場景舵抹,一個應(yīng)用程序中有很多線程需要從RabbitMQ中消費消息或者生產(chǎn)消息肪虎,那么必然需要建立很多個Connection,也就是許多個TCP連接惧蛹。然而對于操作系統(tǒng)而言扇救,建立和銷毀TCP連接都是非常昂貴的開銷刑枝,如果遇到使用高峰,性能瓶頸也隨之顯現(xiàn)迅腔。RabbitMQ采用類似NIO的做法装畅,選擇TCP連接富用,不僅可以減少性能開銷沧烈,同時也便于管理掠兄。
每個線程把持一個信道,所以信道復(fù)用了Connection的TCP連接锌雀。同時RabbitMQ可以確保每個線程的私密性蚂夕,就像擁有獨立的連接一樣。當(dāng)每個信道的流量不是很大時腋逆,復(fù)用單一的Connection可以在產(chǎn)生性能瓶頸的情況下有效地節(jié)省TCP連接資源婿牍。但是當(dāng)信道本身的流量很大時,這時候多個信道復(fù)用一個Connection就會產(chǎn)生性能瓶頸惩歉,進(jìn)而使整體的流量被限制了等脂。此時就需要開辟多個Connection,將這些信道均攤到這些Connection中撑蚌。