RabbitMQ 消息發(fā)送模式詳解
1. 前言
Hello,大家好。今天會為同學們介紹 RabbitMQ 中的消息發(fā)送模式。RabbitMQ 作為一款消息隊列中間件,其提供的消息發(fā)送模式必然是 RabbitMQ 的亮點所在。
理解并掌握 RabbitMQ 中的消息發(fā)送模式,是使用 RabbitMQ 進行消息通信的基礎,同時也是用好 RabbitMQ 的關(guān)鍵所在,所以,請大家務必掌握本節(jié)內(nèi)容,話不多說,就讓我們來看看 RabbitMQ 中都有哪些消息發(fā)送模式,以及消息是如何發(fā)送的吧。
本節(jié)主要內(nèi)容:
-
什么是消息發(fā)送模式;
-
RabbitMQ 中為我們提供了哪些消息發(fā)送模式;
-
RabbitMQ 消息模式詳解及注意事項。
希望各位同學可以理解本節(jié)內(nèi)容,而不是死記硬背。
2. 什么是消息發(fā)送模式 ?
對于消息發(fā)送模式這一名詞,我們先拋開 RabbitMQ 不說,單從字面意義上去理解,很容易知道,消息發(fā)送模式指的就是:消息發(fā)送時,所使用的方法或者中間介質(zhì),換成大白話就是說,消息是通過什么媒介去進行發(fā)送的。
在 RabbitMQ 中,消息發(fā)送模式我們完全可以按照上述所說的來理解,只不過,在 RabbitMQ 中,對消息傳輸所通過的媒介有專業(yè)的術(shù)語罷了。
接下來就讓我們來看一下,在 RabbitMQ 中都有哪些消息發(fā)送模式。
3. RabbitMQ 中為我們提供了哪些消息發(fā)送模式 ?
在 RabbitMQ 中,所有經(jīng)過 RabbitMQ 來傳輸?shù)南?,都需要?jīng)過 RabbitMQ 的隊列來進行傳輸,至于什么是隊列,我在前面的文章中已經(jīng)講過,這里不再贅述。
在介紹 RabbitMQ 中都有哪些消息發(fā)送模式之前,我們需要首先了解,在 RabbitMQ 中的消息發(fā)送模式是如何體現(xiàn)的。
消息在 RabbitMQ 隊列傳輸?shù)倪^程中,根據(jù)不同的傳輸方式,以及所使用的隊列種類的不同,一共劃分了 5 個消息傳輸模式,而這 5 個消息傳輸模式,就是我們所說的消息發(fā)送模式。
根據(jù) RabbitMQ 所實現(xiàn)的消息投遞方式來劃分,可以將消息發(fā)送模式分為兩大類,分別是點對點模式、發(fā)布訂閱模式;根據(jù) RabbitMQ 所采用的隊列方式以及匹配規(guī)則的不同,可以將消息發(fā)送模式分為五大類,分別是普通隊列模式、工作隊列模式、發(fā)布訂閱模式、直接模式、主題模式。
由于按照消息投遞方式所劃分的范圍較廣,我們不能充分了解每個消息發(fā)送模式的內(nèi)容,所以,在介紹消息發(fā)送模式時,我會按照 RabbitMQ 所采用的隊列方式和匹配規(guī)則的不同來進行講解,請同學們做好準備。
4. RabbitMQ 消息模式詳解及注意事項
我們知道,在 RabbitMQ 中,消息的產(chǎn)生是源自生產(chǎn)者,對應的,消費消息是依靠消費者,而在生產(chǎn)者生產(chǎn)消息到消費者最終消費消息的過程中,消息發(fā)送模式扮演著重要的角色。
如果需要將消息發(fā)送模式結(jié)合生產(chǎn)者與消費者進行理解的話,那么,我們可以這樣來理解:在生產(chǎn)者生產(chǎn)出一條消息后,需要經(jīng)過 RabbitMQ 的通道來發(fā)送給消費者,消費者接收到消息,并最終對消息進行消費,這其中的通道,指的就是 RabbitMQ 的消息發(fā)送模式。
接下來讓我們具體來看一下,RabbitMQ 是如何把生產(chǎn)者生產(chǎn)的消息傳輸給消費者消費的。
約定:
由于在本節(jié)中所使用的消息發(fā)送模式原理圖均來自官網(wǎng),所以圖中每個元素都代表什么意思,我在這里一并說過,后面不再贅述:
天藍色橢圓 + 其中的字母 P : 代表生產(chǎn)者。
藍色的橢圓 + 其中的字母 C + 數(shù)字下表 : 代表消費者。
深藍色橢圓 + 其中的字母 X : 代表交換機。
交換機上方的 type : 代表交換機的類型。
橙色小矩形塊所組成的大矩形塊 : 代表具體的一個隊列。
圖中的箭頭,不帶字母標識的 : 代表消息的流向。
圖中箭頭上的字母標識 : 代表特定模式下的 key 值。
4.1 直接模式
定義:
直接模式,即直接發(fā)送消息模式,指的是將消息直接發(fā)送給消費者。
描述:
直接模式允許將多個隊列綁定到一個交換機上,在生產(chǎn)者發(fā)送消息給交換機時,需要攜帶一個 key ,而這個 key 一般被稱為 routing key 或者 binding key,所以直接模式有時也被稱為路由模式。
Tips: RabbitMQ 一般將這個 key 叫做 binding key,但是在實際情況中,出于字面意思,習慣性地將 key 叫做 routing key
直接模式總共分為兩種業(yè)務場景,我們先來看第一種業(yè)務場景,一般被叫做’單 key 綁定’,如下圖所示:

從圖中我們可以看到,交換機的 type 被聲明成了 direct ,這說明我們使用的交換機是直接交換機,即使用的消息發(fā)送模式是直接模式;
orange、black、green 分別表示不同的兩個 routing key ,orange 這一個 key 綁定了一個隊列,black、green、兩個不同的 key 也綁定了一個隊列,這種現(xiàn)象就是直接模式的第一種業(yè)務場景,單 key 綁定。
單 key 綁定的隊列,在生產(chǎn)者生產(chǎn)出消息之后,會根據(jù)不同 key 指向的不同隊列來將消息進行分發(fā),即使是不同的 key 綁定了同一隊列。
我們來看直接模式的最后一個業(yè)務場景,多重 key 綁定,如下圖所示:

在圖中我們可以看到,Q1、Q2 兩個隊列,分別綁定到了 routing key 均為 black 的 direct 交換機上,即名稱相同的一個 key 綁定到了多個隊列上面,這種現(xiàn)象被稱為多重 key 綁定。
在多重 key 綁定下,生產(chǎn)者生產(chǎn)的消息均會被發(fā)送到相同 key 值所綁定的隊列上面,這里需要同學們注意。
實現(xiàn)偽代碼:
// 生產(chǎn)者
channel.basicPublish(EXCHANGE_NAME, "Routing Key", ...)
// 消費者
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "Routing Key"
代碼解釋:
第 2 行,我們使用 channel 的 basicPublish 方法來指定第二個參數(shù),即我們的 routing key 的名稱。
第 5 行,我們使用 channel 的 queueBind 方式來將消息隊列綁定到名為 Routing Key 的直接模式交換機上。
Tips:
1. 一般,我們在使用 RabbitMQ 時,默認會使用直接模式來發(fā)送消息,直接模式也是使用最多的消息發(fā)送模式,如果其他消息發(fā)送模式不能理解,則務必理解直接模式;
2. 在使用直接模式時,一定不要忘了指定 routing key ,否則,將不能使用直接模式來發(fā)送消息;
3. 當我們綁定了多個隊列到交換機上時,一旦消息被發(fā)送,則符合同一 routing key 的隊列都會接收到消息。
4.2 發(fā)布訂閱模式
定義:
發(fā)布訂閱模式,即生產(chǎn)者發(fā)布消息,消費者通過訂閱的方式來消費消息。
其實,發(fā)布訂閱模式在我看來,不過是給傳統(tǒng)的發(fā)送和接收起一個高大上的名字罷了,本質(zhì)上仍熱是消息的生產(chǎn)和消費,只不過這種模式更像與發(fā)布和訂閱,因此得名發(fā)布訂閱模式。
描述:
發(fā)布訂閱模式只有一種實際的業(yè)務場景,我們把他稱為群發(fā)模式。

上圖所示場景也是發(fā)布訂閱模式中的一種,但是這種模式?jīng)]有任何存在意義,因為在這種模式下,生產(chǎn)者生產(chǎn)出一條消息之后,將消息直接發(fā)送到了交換機上,大家注意看,此時的交換機上沒有綁定任何消息隊列,所以,此時位于交換機上的消息將丟失,消費者無法拿到消息進行消費。
接下來讓我們看看實際上的發(fā)布訂閱模式:

生產(chǎn)者生產(chǎn)一條消息后,將消息首先發(fā)送到交換機上,交換機進行檢測,發(fā)現(xiàn)存在兩個隊列都綁定在自身上面,于是,將消息全部投遞到所綁定的隊列上面,最后再由消費者接收消息并消費。
發(fā)布訂閱模式的特點,就是一個生產(chǎn)者、一個交換機、多個隊列、多個消費者,由于生產(chǎn)者生產(chǎn)出來的消息會發(fā)送到綁定在交換機上的所有隊列上,這種場景很類似于我們給很多人群發(fā)消息,所以,這種模式被叫做群發(fā)模式。
實現(xiàn)偽代碼:
// 生產(chǎn)者
channel.exchangeDeclare(EXCHANGE_NAME, "fanout")
// 消費者
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "")
channel.basicConsume(QUEUE_NAME, false, consumer)
代碼解釋:
第 2 行,我們使用 channel 的 exchangeDeclare 方法,將交換機的類型指定為 fanout 交換機,這是使用發(fā)布訂閱模式的前提。
第 5 行,我們使用 channel 的 queueBind 方法,為交換機綁定一個隊列,如果在發(fā)布訂閱模式下,不綁定消息隊列到交換機上,則消息會丟失,消費者接收不到任何消息。
第 6 行,我們使用 channel 的 basicConsume 方法,來接收由消息隊列發(fā)送的消息并消費。
Tips:
1.由于沒有綁定隊列到交換機上的這種方式會丟失消息,所以在實際工作中,這個方法幾乎從不使用,因為沒有任何意義;
2.使用發(fā)布訂閱模式,在給交換機綁定隊列時,不要手動指定隊列的 key 值,因為 RabbitMQ 會自動生成相同的 key 值;
3. 發(fā)布訂閱模式一般用于不用指定特殊的 key 值,且需要消息批量發(fā)送的業(yè)務場景。
4.3 普通隊列模式
定義:
普通隊列模式,即最簡單的消息發(fā)送模式,不使用任何交換機,由生產(chǎn)者、隊列、消費者組合完成消息的發(fā)送和接收。
描述:
普通隊列模式,由于其操作簡單,所以又被稱為簡單模式,如下圖所示:

普通隊列模式,在生產(chǎn)者生產(chǎn)完消息之后,直接將消息發(fā)送到隊列中去,不經(jīng)過交換機進行處理,然后由消費者直接接收消息并消費。在這個過程中間,沒有我們需要特別注意的地方。
實現(xiàn)偽代碼:
// 生產(chǎn)者
channel.queueDeclare(QUEUE_NAME, false, false, false, null)
// 消費者
channel.basicConsume(QUEUE_NAME, true, consumer)
代碼解釋:
第 2 行,我們使用 channel 的 queueDeclare 方法來為通過綁定消息隊列,并指定消息隊列的名稱。
第 5 行,我們是使用 channel 的 basicConsume 方法來直接從隊列接收消息,并自動監(jiān)聽消費。
Tips:
1. 普通隊列模式操作簡單,適合很簡單的業(yè)務場景,同時,初學者更易于理解。
2. 由于普通隊列模式所能實現(xiàn)的業(yè)務場景太過簡單,所以在實際業(yè)務場景中,很少會用到。
4.4 工作隊列模式
定義:
工作隊列模式,和普通隊列模式有點像,都是不使用任何交換機,由生產(chǎn)者、隊列、消費者組合完成消息的發(fā)送和接收,只不過工作隊列支持存在多個消費者,而普通隊列模式只支持一個消費者。
描述:

工作隊列模式下,生產(chǎn)者生產(chǎn)出消息后,直接將消息發(fā)送到消息隊列中,然后多個消費者按照一個隨機的順序來依次接收消息并消費,存在多個消費者消費消息時,下一個消費者只能等待上一個消費者消費結(jié)束后才能接收消息并進行消費。
這就提示我們,在實際工作中,我們可以把費時的業(yè)務操作交給 RabbitMQ 去做,這樣可以提升代碼的執(zhí)行效率。
實現(xiàn)偽代碼:
// 生產(chǎn)者
channel.basicPublish(QUEUE_NAME, null, message.getBytes());
Thread.sleep(1000);
// 消費者
Delivery delivery = consumer.nextDelivery();
Thread.sleep(1000);
代碼解釋:
第 2 行,我們使用 channel 的 basicPublish 方法來生成一條消息。
第 3 行,在生成一條消息之后,我們等待 1000 毫秒,即 1 秒后再次生成一條消息。
第 5 行,我們使用 consumer 的 nextDelivery 方法來依次獲取生產(chǎn)者生產(chǎn)的消息。
第 6 行,在消費完一條消息之后,我們讓下一個消費者等待 1 秒鐘,再去消費下一條消息。
Tips:
1.工作隊列模式與發(fā)布訂閱模式有相同之處,既他們都是經(jīng)過一個隊列來向多個消費者發(fā)送消息,不同之處在于,前者不用綁定交換機,而后者則需要使用交換機;
2.應用工作隊列模式,一定要根據(jù)實際業(yè)務需求和實際業(yè)務場景,設置好多個消費者間等待消費消息的時間,如果這個間隔時間設置太久,則容易造成下一個消費者持續(xù)等待,嚴重占用CPU資源,如果設置時間太短,則業(yè)務邏輯還沒執(zhí)行完成就開始了下一個消息的消費,這兩種業(yè)務場景都是不應該出現(xiàn)的
4.5 主題模式
定義:
主題模式,也被稱為通配符模式,官網(wǎng)一般稱為主題模式,即交換機與消息隊列所綁定的 key 值可以像匹配通配符的方式,來匹配消息隊列,到底什么意思呢,我們往下看。
描述:

主題模式對 routing key 的匹配規(guī)則做了改進,上述其他四種模式中有涉及 key 匹配的地方都是完全匹配,即名稱必須相等時才能把 key 匹配上,而對于主題模式,則不需要這樣。
主題模式將 key 值中的每個單詞或者關(guān)鍵詞,使用英文狀態(tài)下的 . 符號進行間隔,如上圖所示。上圖中為我們列舉了主題模式中支持的所有通配符語法,我們一個一個來介紹:
Tips: 由于 * 號是 MD 語法關(guān)鍵字,所以這里暫時用 ^ 號代替,同學們注意。
.orange. : 表示在 orange 的兩側(cè)可以匹配一個 key 值,例如 123.orange.456 、abc.orange.456 等,但是,abc.orange.456.123 這個是不可以的。
..rabbit : 用法和上述 orange 相同,例如: 123.abc.rabbit 、abd.acd.rabbit 等。
lazy.# : 表示在 lazy 的右側(cè),可以匹配多個 key 值可以進行通配符匹配,例如:lazy.abc.123 等。
實現(xiàn)偽代碼:
// 生產(chǎn)者
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
channel.basicPublish(EXCHANGE_NAME, "key.123", ...);
// 消費者
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key.*");
代碼解釋:
第 2 行,我們使用 channel 的 exchangeDeclare 方法,將交換機聲明為 topic 類型,這是使用主題模式的必須項。
第 3 行,我們使用 channel 的 basicPublish 方法來將消息綁定到交換機上。
第 6 行,我們使用 channel 的 queueBind 方法,將消息隊列綁定到交換機上,并且設置 key 的匹配策略為 key.* 。
Tips:
1. 使用主題模式一定要很清楚每個通配符所代表的意思,以防用錯通配符,引起不必要的錯誤;
2. 主題模式為我們提供了類似于模糊搜索的功能,當我們不知道如何設置 key 時,我們可以采用主題模式,同時,主題模式為我們 key 的分組也提供了很好地實現(xiàn)方案。
3. 使用主題模式時,注意 key 值的命名不要太長,也不要太短。
5. 小結(jié)

本小節(jié)從什么是消息發(fā)送模式開始,詳細介紹了 RabbitMQ 中的 5 種消息發(fā)送模式,對于每一種消息發(fā)送模式,采用文字+偽代碼+圖片的方式進行了全方位的講解,旨在幫助同學們,通過對本節(jié)內(nèi)容的學習,可以充分了解 RabbitMQ 中都有哪些消息發(fā)送模式,以及每種消息發(fā)送模式的特點、消息發(fā)送原理等。
本小節(jié)是用好 RabbitMQ 的基礎,在學習本節(jié)內(nèi)容時,一定要理清楚 5 中消息發(fā)送模式間的區(qū)別與聯(lián)系,我們只有在充分了解了 RabbitMQ 中的消息發(fā)送模式之后,才能很清楚的知道,什么業(yè)務場景哪種消息模式最合適,最后,希望同學們學的開心、碼的快樂。