SpringBoot中RabbitMQ集群的搭建詳解
單個(gè)的 RabbitMQ 肯定無法實(shí)現(xiàn)高可用,要想高可用,還得上集群。
今天松哥就來和大家聊一聊 RabbitMQ 集群的搭建。
1. 兩種模式
說到集群,小伙伴們可能第一個(gè)問題是,如果我有一個(gè) RabbitMQ 集群,那么是不是我的消息集群中的每一個(gè)實(shí)例都保存一份呢?
這其實(shí)就涉及到 RabbitMQ 集群的兩種模式:
- 普通集群
- 鏡像集群
1.1 普通集群
普通集群模式,就是將 RabbitMQ 部署到多臺(tái)服務(wù)器上,每個(gè)服務(wù)器啟動(dòng)一個(gè) RabbitMQ 實(shí)例,多個(gè)實(shí)例之間進(jìn)行消息通信。
此時(shí)我們創(chuàng)建的隊(duì)列 Queue,它的元數(shù)據(jù)(主要就是 Queue 的一些配置信息)會(huì)在所有的 RabbitMQ 實(shí)例中進(jìn)行同步,但是隊(duì)列中的消息只會(huì)存在于一個(gè) RabbitMQ 實(shí)例上,而不會(huì)同步到其他隊(duì)列。
當(dāng)我們消費(fèi)消息的時(shí)候,如果連接到了另外一個(gè)實(shí)例,那么那個(gè)實(shí)例會(huì)通過元數(shù)據(jù)定位到 Queue 所在的位置,然后訪問 Queue 所在的實(shí)例,拉取數(shù)據(jù)過來發(fā)送給消費(fèi)者。
這種集群可以提高 RabbitMQ 的消息吞吐能力,但是無法保證高可用,因?yàn)橐坏┮粋€(gè) RabbitMQ 實(shí)例掛了,消息就沒法訪問了,如果消息隊(duì)列做了持久化,那么等 RabbitMQ 實(shí)例恢復(fù)后,就可以繼續(xù)訪問了;如果消息隊(duì)列沒做持久化,那么消息就丟了。
大致的流程圖如下圖:
1.2 鏡像集群
它和普通集群最大的區(qū)別在于 Queue 數(shù)據(jù)和原數(shù)據(jù)不再是單獨(dú)存儲(chǔ)在一臺(tái)機(jī)器上,而是同時(shí)存儲(chǔ)在多臺(tái)機(jī)器上。也就是說每個(gè) RabbitMQ 實(shí)例都有一份鏡像數(shù)據(jù)(副本數(shù)據(jù))。每次寫入消息的時(shí)候都會(huì)自動(dòng)把數(shù)據(jù)同步到多臺(tái)實(shí)例上去,這樣一旦其中一臺(tái)機(jī)器發(fā)生故障,其他機(jī)器還有一份副本數(shù)據(jù)可以繼續(xù)提供服務(wù),也就實(shí)現(xiàn)了高可用。
大致流程圖如下圖:
1.3 節(jié)點(diǎn)類型
RabbitMQ 中的節(jié)點(diǎn)類型有兩種:
- RAM node:內(nèi)存節(jié)點(diǎn)將所有的隊(duì)列、交換機(jī)、綁定、用戶、權(quán)限和 vhost 的元數(shù)據(jù)定義存儲(chǔ)在內(nèi)存中,好處是可以使得交換機(jī)和隊(duì)列聲明等操作速度更快。
- Disk node:將元數(shù)據(jù)存儲(chǔ)在磁盤中,單節(jié)點(diǎn)系統(tǒng)只允許磁盤類型的節(jié)點(diǎn),防止重啟 RabbitMQ 的時(shí)候,丟失系統(tǒng)的配置信息
RabbitMQ 要求在集群中至少有一個(gè)磁盤節(jié)點(diǎn),所有其他節(jié)點(diǎn)可以是內(nèi)存節(jié)點(diǎn),當(dāng)節(jié)點(diǎn)加入或者離開集群時(shí),必須要將該變更通知到至少一個(gè)磁盤節(jié)點(diǎn)。如果集群中唯一的一個(gè)磁盤節(jié)點(diǎn)崩潰的話,集群仍然可以保持運(yùn)行,但是無法進(jìn)行其他操作(增刪改查),直到節(jié)點(diǎn)恢復(fù)。為了確保集群信息的可靠性,或者在不確定使用磁盤節(jié)點(diǎn)還是內(nèi)存節(jié)點(diǎn)的時(shí)候,建議直接用磁盤節(jié)點(diǎn)。
2. 搭建普通集群
2.1 預(yù)備知識(shí)
大致的結(jié)構(gòu)了解了,接下來我們就把集群給搭建起來。先從普通集群開始,我們就使用 docker 來搭建。
搭建之前,有兩個(gè)預(yù)備知識(shí)需要大家了解:
- 搭建集群時(shí),節(jié)點(diǎn)中的 Erlang Cookie 值要一致,默認(rèn)情況下,文件在 /var/lib/rabbitmq/.erlang.cookie,我們?cè)谟?docker 創(chuàng)建 RabbitMQ 容器時(shí),可以為之設(shè)置相應(yīng)的 Cookie 值。
- RabbitMQ 是通過主機(jī)名來連接服務(wù),必須保證各個(gè)主機(jī)名之間可以 ping 通??梢酝ㄟ^編輯 /etc/hosts 來手工添加主機(jī)名和 IP 對(duì)應(yīng)關(guān)系。如果主機(jī)名 ping 不通,RabbitMQ 服務(wù)啟動(dòng)會(huì)失?。ㄈ绻覀兪窃诓煌姆?wù)器上搭建 RabbitMQ 集群,大家需要注意這一點(diǎn),接下來的 2.2 小結(jié),我們將通過 Docker 的容器連接 link 來實(shí)現(xiàn)容器之間的訪問,略有不同)。
2.2 開始搭建
執(zhí)行如下命令創(chuàng)建三個(gè) RabbitMQ 容器:
docker run -d --hostname rabbit01 --name mq01 -p 5671:5672 -p 15671:15672 -e RABBITMQ_ERLANG_COOKIE="javaboy_rabbitmq_cookie" rabbitmq:3-management docker run -d --hostname rabbit02 --name mq02 --link mq01:mylink01 -p 5672:5672 -p 15672:15672 -e RABBITMQ_ERLANG_COOKIE="javaboy_rabbitmq_cookie" rabbitmq:3-management docker run -d --hostname rabbit03 --name mq03 --link mq01:mylink02 --link mq02:mylink03 -p 5673:5672 -p 15673:15672 -e RABBITMQ_ERLANG_COOKIE="javaboy_rabbitmq_cookie" rabbitmq:3-management
運(yùn)行結(jié)果如下:
三個(gè)節(jié)點(diǎn)現(xiàn)在就啟動(dòng)好了,注意在 mq02 和 mq03 中,分別使用了 --link
參數(shù)來實(shí)現(xiàn)容器連接,關(guān)于這個(gè)參數(shù),如果大家不懂,可以在公眾號(hào)江南一點(diǎn)雨后臺(tái)回復(fù) docker,由松哥寫的 docker 入門教程,里邊有講這個(gè)。這里我就不啰嗦了。另外還需要注意,mq03 容器中要既能夠連接 mq01 也能夠連接 mq02。
接下來進(jìn)入到 mq02 容器中,首先查看一下 hosts 文件,可以看到我們配置的容器連接已經(jīng)生效了:
將來在 mq02 容器中,就可以通過 mylink01 或者 rabbit01 訪問到 mq01 容器了。
接下來我們開始集群的配置。
分別執(zhí)行如下命令將 mq02 容器加入集群中:
rabbitmqctl stop_app rabbitmqctl join_cluster rabbit@rabbit01 rabbitmqctl start_app
接下來輸入如下命令我們可以查看集群的狀態(tài):
rabbitmqctl cluster_status
可以看到,集群中已經(jīng)有兩個(gè)節(jié)點(diǎn)了。
接下來通過相同的方式將 mq03 也加入到集群中:
rabbitmqctl stop_app rabbitmqctl join_cluster rabbit@rabbit01 rabbitmqctl start_app
接下來,我們可以查看集群信息:
可以看到,此時(shí)集群中已經(jīng)有三個(gè)節(jié)點(diǎn)了。
其實(shí),這個(gè)時(shí)候,我們也可以通過網(wǎng)頁來查看集群信息,在三個(gè) RabbitMQ 實(shí)例的 Web 端首頁,都可以看到如下內(nèi)容:
2.3 代碼測(cè)試
接下來我們來簡(jiǎn)單測(cè)試一下這個(gè)集群。
我們創(chuàng)建一個(gè)名為 mq_cluster_demo 的父工程,然后在其中創(chuàng)建兩個(gè)子工程。
第一個(gè)子工程名為 provider,是一個(gè)消息生產(chǎn)者,創(chuàng)建時(shí)引入 Web 和 RabbitMQ 依賴,如下:
然后配置 applicaiton.properties,內(nèi)容如下(注意集群配置):
spring.rabbitmq.addresses=localhost:5671,localhost:5672,localhost:5673 spring.rabbitmq.username=guest spring.rabbitmq.password=guest
接下來提供一個(gè)簡(jiǎn)單的隊(duì)列,如下:
@Configuration public class RabbitConfig { public static final String MY_QUEUE_NAME = "my_queue_name"; public static final String MY_EXCHANGE_NAME = "my_exchange_name"; public static final String MY_ROUTING_KEY = "my_queue_name"; @Bean Queue queue() { return new Queue(MY_QUEUE_NAME, true, false, false); } @Bean DirectExchange directExchange() { return new DirectExchange(MY_EXCHANGE_NAME, true, false); } @Bean Binding binding() { return BindingBuilder.bind(queue()) .to(directExchange()) .with(MY_ROUTING_KEY); } }
這個(gè)沒啥好說的,都是基本內(nèi)容,接下來我們?cè)趩卧獪y(cè)試中進(jìn)行消息發(fā)送測(cè)試:
@SpringBootTest class ProviderApplicationTests { @Autowired RabbitTemplate rabbitTemplate; @Test void contextLoads() { rabbitTemplate.convertAndSend(null, RabbitConfig.MY_QUEUE_NAME, "hello 江南一點(diǎn)雨"); } }
這條消息發(fā)送成功之后,在 RabbitMQ 的 Web 管理端,我們會(huì)看到三個(gè) RabbitMQ 實(shí)例上都會(huì)顯示有一條消息,但是實(shí)際上消息本身只存在于一個(gè) RabbitMQ 實(shí)例。
接下來我們?cè)賱?chuàng)建一個(gè)消息消費(fèi)者,消息消費(fèi)者的依賴以及配置和消息生產(chǎn)者都是一模一樣,我就不重復(fù)了,消息消費(fèi)者中增加一個(gè)消息接收器:
@Component public class MsgReceiver { @RabbitListener(queues = RabbitConfig.MY_QUEUE_NAME) public void handleMsg(String msg) { System.out.println("msg = " + msg); } }
當(dāng)消息消費(fèi)者啟動(dòng)成功后,這個(gè)方法中只收到一條消息,進(jìn)一步驗(yàn)證了我們搭建的 RabbitMQ 集群是沒問題的。
2.4 反向測(cè)試
接下來松哥再舉兩個(gè)反例,以證明消息并沒有同步到其他 RabbitMQ 實(shí)例。
確保三個(gè) RabbitMQ 實(shí)例都是啟動(dòng)狀態(tài),關(guān)閉掉 Consumer,然后通過 provider 發(fā)送一條消息,發(fā)送成功之后,關(guān)閉 mq01 實(shí)例,然后啟動(dòng) Consumer 實(shí)例,此時(shí) Consumer 實(shí)例并不會(huì)消費(fèi)消息,反而會(huì)報(bào)錯(cuò)說 mq01 實(shí)例連接不上,這個(gè)例子就可以說明消息在 mq01 上,并沒有同步到另外兩個(gè) MQ 上。相反,如果 provider 發(fā)送消息成功之后,我們沒有關(guān)閉 mq01 實(shí)例而是關(guān)閉了 mq02 實(shí)例,那么你就會(huì)發(fā)現(xiàn)消息的消費(fèi)不受影響。
3. 搭建鏡像集群
所謂的鏡像集群模式并不需要額外搭建,只需要我們將隊(duì)列配置為鏡像隊(duì)列即可。
這個(gè)配置可以通過網(wǎng)頁配置,也可以通過命令行配置,我們分別來看。
3.1 網(wǎng)頁配置鏡像隊(duì)列
先來看看網(wǎng)頁上如何配置鏡像隊(duì)列。
點(diǎn)擊 Admin 選項(xiàng)卡,然后點(diǎn)擊右邊的 Policies,再點(diǎn)擊 Add/update a policy
,如下圖:
接下來添加一個(gè)策略,如下圖:
各參數(shù)含義如下:
Name: policy 的名稱。
Pattern: queue 的匹配模式(正則表達(dá)式)。
Definition:鏡像定義,主要有三個(gè)參數(shù):ha-mode, ha-params, ha-sync-mode。
- ha-mode:指明鏡像隊(duì)列的模式,有效值為 all、exactly、nodes。其中 all 表示在集群中所有的節(jié)點(diǎn)上進(jìn)行鏡像(默認(rèn)即此);exactly 表示在指定個(gè)數(shù)的節(jié)點(diǎn)上進(jìn)行鏡像,節(jié)點(diǎn)的個(gè)數(shù)由 ha-params 指定;nodes 表示在指定的節(jié)點(diǎn)上進(jìn)行鏡像,節(jié)點(diǎn)名稱通過 ha-params 指定。
- ha-params:ha-mode 模式需要用到的參數(shù)。
- ha-sync-mode:進(jìn)行隊(duì)列中消息的同步方式,有效值為 automatic 和 manual。
priority 為可選參數(shù),表示 policy 的優(yōu)先級(jí)。
配置完成后,點(diǎn)擊下面的 add/update policy
按鈕,完成策略的添加,如下:
添加完成后,我們可以進(jìn)行一個(gè)簡(jiǎn)單的測(cè)試。
首先確認(rèn)三個(gè) RabbitMQ 都啟動(dòng)了,然后用上面的 provider 向消息隊(duì)列發(fā)送一條消息。
發(fā)完之后關(guān)閉 mq01 實(shí)例。
接下來啟動(dòng) consumer,此時(shí)發(fā)現(xiàn) consumer 可以完成消息的消費(fèi)(注意和前面的反向測(cè)試區(qū)分),這就說明鏡像隊(duì)列已經(jīng)搭建成功了。
3.2 命令行配置鏡像隊(duì)列
命令行的配置格式如下:
rabbitmqctl set_policy [-p vhost] [--priority priority] [--apply-to apply-to] {name} {pattern} {definition}
舉一個(gè)簡(jiǎn)單的配置案例:
rabbitmqctl set_policy -p / --apply-to queues my_queue_mirror "^" '{"ha-mode":"all","ha-sync-mode":"automatic"}'
4. 小結(jié)
好啦,這就是松哥和大家分享的 RabbitMQ 中的集群搭建,感興趣的小伙伴趕緊去試試吧~
以上就是SpringBoot中RabbitMQ集群的搭建詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot RabbitMQ集群搭建的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- springboot整合rabbitmq的示例代碼
- springboot集成rabbitMQ之對(duì)象傳輸?shù)姆椒?/a>
- springboot實(shí)現(xiàn)rabbitmq的隊(duì)列初始化和綁定
- SpringBoot+RabbitMq具體使用的幾種姿勢(shì)
- SpringBoot使用RabbitMQ延時(shí)隊(duì)列(小白必備)
- springboot + rabbitmq 如何實(shí)現(xiàn)消息確認(rèn)機(jī)制(踩坑經(jīng)驗(yàn))
- SpringBoot中連接多個(gè)RabbitMQ的方法詳解
- SpringBoot實(shí)現(xiàn)RabbitMQ監(jiān)聽消息的四種方式
- SpringBoot整合RabbitMQ之路由模式的實(shí)現(xiàn)
相關(guān)文章
Java concurrency線程池之Callable和Future_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了Java concurrency線程池之Callable和Future,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06java實(shí)用小技巧之判斷l(xiāng)ist是否有重復(fù)項(xiàng)簡(jiǎn)單例子
這篇文章主要給大家介紹了關(guān)于java實(shí)用小技巧之判斷l(xiāng)ist是否有重復(fù)項(xiàng)的相關(guān)資料,在開發(fā)工作中我們有時(shí)需要去判斷List集合中是否含有重復(fù)的元素,需要的朋友可以參考下2023-10-10基于SpringBoot實(shí)現(xiàn)輕量級(jí)的動(dòng)態(tài)定時(shí)任務(wù)調(diào)度的方法
本文介紹了如何在SpringBoot框架中實(shí)現(xiàn)輕量級(jí)的動(dòng)態(tài)定時(shí)任務(wù)調(diào)度,通過將任務(wù)以類為基礎(chǔ)單位,并通過配置數(shù)據(jù)進(jìn)行任務(wù)讀取和反射生成任務(wù)對(duì)象,感興趣的朋友跟隨小編一起看看吧2024-11-11Java中HTTP接口請(qǐng)求重試的實(shí)現(xiàn)方式
HTTP接口請(qǐng)求重試是指在請(qǐng)求失敗時(shí),再次發(fā)起請(qǐng)求的機(jī)制,在實(shí)際應(yīng)用中,由于網(wǎng)絡(luò)波動(dòng)、服務(wù)器故障等原因,HTTP接口請(qǐng)求可能會(huì)失敗,為了保證系統(tǒng)的可用性和穩(wěn)定性,需要對(duì)HTTP接口請(qǐng)求進(jìn)行重試,所以本文給大家介紹了HTTP接口請(qǐng)求重試的實(shí)現(xiàn)方式,需要的朋友可以參考下2024-01-01