手把手教你搭建 RabbitMQ 集群
文章目錄
1. 兩種模式
1.1 普通集群
1.2 鏡像集群
1.3 節(jié)點類型
2. 搭建普通集群
2.1 預(yù)備知識
2.2 開始搭建
2.3 代碼測試
2.4 反向測試
3. 搭建鏡像集群
3.1 網(wǎng)頁配置鏡像隊列
3.2 命令行配置鏡像隊列
4. 小結(jié)
今天松哥就來和大家聊一聊 RabbitMQ 集群的搭建。
1. 兩種模式
說到集群,小伙伴們可能第一個問題是,如果我有一個 RabbitMQ 集群,那么是不是我的消息集群中的每一個實例都保存一份呢?
這其實就涉及到 RabbitMQ 集群的兩種模式:
普通集群
鏡像集群
1.1 普通集群
普通集群模式,就是將 RabbitMQ 部署到多臺服務(wù)器上,每個服務(wù)器啟動一個 RabbitMQ 實例,多個實例之間進行消息通信。
此時我們創(chuàng)建的隊列 Queue,它的元數(shù)據(jù)(主要就是 Queue 的一些配置信息)會在所有的 RabbitMQ 實例中進行同步,但是隊列中的消息只會存在于一個 RabbitMQ 實例上,而不會同步到其他隊列。
當(dāng)我們消費消息的時候,如果連接到了另外一個實例,那么那個實例會通過元數(shù)據(jù)定位到 Queue 所在的位置,然后訪問 Queue 所在的實例,拉取數(shù)據(jù)過來發(fā)送給消費者。
這種集群可以提高 RabbitMQ 的消息吞吐能力,但是無法保證高可用,因為一旦一個 RabbitMQ 實例掛了,消息就沒法訪問了,如果消息隊列做了持久化,那么等 RabbitMQ 實例恢復(fù)后,就可以繼續(xù)訪問了;如果消息隊列沒做持久化,那么消息就丟了。
大致的流程圖如下圖:
1.2 鏡像集群
它和普通集群最大的區(qū)別在于 Queue 數(shù)據(jù)和原數(shù)據(jù)不再是單獨存儲在一臺機器上,而是同時存儲在多臺機器上。也就是說每個 RabbitMQ 實例都有一份鏡像數(shù)據(jù)(副本數(shù)據(jù))。每次寫入消息的時候都會自動把數(shù)據(jù)同步到多臺實例上去,這樣一旦其中一臺機器發(fā)生故障,其他機器還有一份副本數(shù)據(jù)可以繼續(xù)提供服務(wù),也就實現(xiàn)了高可用。
大致流程圖如下圖:
1.3 節(jié)點類型
RabbitMQ 中的節(jié)點類型有兩種:
RAM node:內(nèi)存節(jié)點將所有的隊列、交換機、綁定、用戶、權(quán)限和 vhost 的元數(shù)據(jù)定義存儲在內(nèi)存中,好處是可以使得交換機和隊列聲明等操作速度更快。
Disk node:將元數(shù)據(jù)存儲在磁盤中,單節(jié)點系統(tǒng)只允許磁盤類型的節(jié)點,防止重啟 RabbitMQ 的時候,丟失系統(tǒng)的配置信息
RabbitMQ 要求在集群中至少有一個磁盤節(jié)點,所有其他節(jié)點可以是內(nèi)存節(jié)點,當(dāng)節(jié)點加入或者離開集群時,必須要將該變更通知到至少一個磁盤節(jié)點。如果集群中唯一的一個磁盤節(jié)點崩潰的話,集群仍然可以保持運行,但是無法進行其他操作(增刪改查),直到節(jié)點恢復(fù)。為了確保集群信息的可靠性,或者在不確定使用磁盤節(jié)點還是內(nèi)存節(jié)點的時候,建議直接用磁盤節(jié)點。
2. 搭建普通集群
2.1 預(yù)備知識
大致的結(jié)構(gòu)了解了,接下來我們就把集群給搭建起來。先從普通集群開始,我們就使用 docker 來搭建。
搭建之前,有兩個預(yù)備知識需要大家了解:
搭建集群時,節(jié)點中的 Erlang Cookie 值要一致,默認情況下,文件在 /var/lib/rabbitmq/.erlang.cookie,我們在用 docker 創(chuàng)建 RabbitMQ 容器時,可以為之設(shè)置相應(yīng)的 Cookie 值。
RabbitMQ 是通過主機名來連接服務(wù),必須保證各個主機名之間可以 ping 通。可以通過編輯 /etc/hosts 來手工添加主機名和 IP 對應(yīng)關(guān)系。如果主機名 ping 不通,RabbitMQ 服務(wù)啟動會失敗(如果我們是在不同的服務(wù)器上搭建 RabbitMQ 集群,大家需要注意這一點,接下來的 2.2 小結(jié),我們將通過 Docker 的容器連接 link 來實現(xiàn)容器之間的訪問,略有不同)。
2.2 開始搭建
執(zhí)行如下命令創(chuàng)建三個 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
1
2
3
運行結(jié)果如下:
三個節(jié)點現(xiàn)在就啟動好了,注意在 mq02 和 mq03 中,分別使用了 --link 參數(shù)來實現(xiàn)容器連接,關(guān)于這個參數(shù),如果大家不懂,可以在公眾號江南一點雨后臺回復(fù) docker,由松哥寫的 docker 入門教程,里邊有講這個。這里我就不啰嗦了。另外還需要注意,mq03 容器中要既能夠連接 mq01 也能夠連接 mq02。
接下來進入到 mq02 容器中,首先查看一下 hosts 文件,可以看到我們配置的容器連接已經(jīng)生效了:
將來在 mq02 容器中,就可以通過 mylink01 或者 rabbit01 訪問到 mq01 容器了。
接下來我們開始集群的配置。
分別執(zhí)行如下命令將 mq02 容器加入集群中:
rabbitmqctl stop_app rabbitmqctl join_cluster rabbit@rabbit01 rabbitmqctl start_app
1
2
3
接下來輸入如下命令我們可以查看集群的狀態(tài):
rabbitmqctl cluster_status
1
可以看到,集群中已經(jīng)有兩個節(jié)點了。
接下來通過相同的方式將 mq03 也加入到集群中:
rabbitmqctl stop_app rabbitmqctl join_cluster rabbit@rabbit01 rabbitmqctl start_app
1
2
3
接下來,我們可以查看集群信息:
可以看到,此時集群中已經(jīng)有三個節(jié)點了。
其實,這個時候,我們也可以通過網(wǎng)頁來查看集群信息,在三個 RabbitMQ 實例的 Web 端首頁,都可以看到如下內(nèi)容:
2.3 代碼測試
接下來我們來簡單測試一下這個集群。
我們創(chuàng)建一個名為 mq_cluster_demo 的父工程,然后在其中創(chuàng)建兩個子工程。
第一個子工程名為 provider,是一個消息生產(chǎn)者,創(chuàng)建時引入 Web 和 RabbitMQ 依賴,如下:
然后配置 applicaiton.properties,內(nèi)容如下(注意集群配置):
spring.rabbitmq.addresses=localhost:5671,localhost:5672,localhost:5673 spring.rabbitmq.username=guest spring.rabbitmq.password=guest
1
2
3
接下來提供一個簡單的隊列,如下:
@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); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
這個沒啥好說的,都是基本內(nèi)容,接下來我們在單元測試中進行消息發(fā)送測試:
@SpringBootTest class ProviderApplicationTests { @Autowired RabbitTemplate rabbitTemplate; @Test void contextLoads() { rabbitTemplate.convertAndSend(null, RabbitConfig.MY_QUEUE_NAME, "hello 江南一點雨"); } }
1
2
3
4
5
6
7
8
9
10
11
12
這條消息發(fā)送成功之后,在 RabbitMQ 的 Web 管理端,我們會看到三個 RabbitMQ 實例上都會顯示有一條消息,但是實際上消息本身只存在于一個 RabbitMQ 實例。
接下來我們再創(chuàng)建一個消息消費者,消息消費者的依賴以及配置和消息生產(chǎn)者都是一模一樣,我就不重復(fù)了,消息消費者中增加一個消息接收器:
@Component public class MsgReceiver { @RabbitListener(queues = RabbitConfig.MY_QUEUE_NAME) public void handleMsg(String msg) { System.out.println("msg = " + msg); } }
1
2
3
4
5
6
7
8
當(dāng)消息消費者啟動成功后,這個方法中只收到一條消息,進一步驗證了我們搭建的 RabbitMQ 集群是沒問題的。
2.4 反向測試
接下來松哥再舉兩個反例,以證明消息并沒有同步到其他 RabbitMQ 實例。
確保三個 RabbitMQ 實例都是啟動狀態(tài),關(guān)閉掉 Consumer,然后通過 provider 發(fā)送一條消息,發(fā)送成功之后,關(guān)閉 mq01 實例,然后啟動 Consumer 實例,此時 Consumer 實例并不會消費消息,反而會報錯說 mq01 實例連接不上,這個例子就可以說明消息在 mq01 上,并沒有同步到另外兩個 MQ 上。相反,如果 provider 發(fā)送消息成功之后,我們沒有關(guān)閉 mq01 實例而是關(guān)閉了 mq02 實例,那么你就會發(fā)現(xiàn)消息的消費不受影響。
3. 搭建鏡像集群
所謂的鏡像集群模式并不需要額外搭建,只需要我們將隊列配置為鏡像隊列即可。
這個配置可以通過網(wǎng)頁配置,也可以通過命令行配置,我們分別來看。
3.1 網(wǎng)頁配置鏡像隊列
先來看看網(wǎng)頁上如何配置鏡像隊列。
點擊 Admin 選項卡,然后點擊右邊的 Policies,再點擊 Add/update a policy,如下圖:
接下來添加一個策略,如下圖:
各參數(shù)含義如下:
Name: policy 的名稱。
Pattern: queue 的匹配模式(正則表達式)。
Definition:鏡像定義,主要有三個參數(shù):ha-mode, ha-params, ha-sync-mode。
ha-mode:指明鏡像隊列的模式,有效值為 all、exactly、nodes。其中 all 表示在集群中所有的節(jié)點上進行鏡像(默認即此);exactly 表示在指定個數(shù)的節(jié)點上進行鏡像,節(jié)點的個數(shù)由 ha-params 指定;nodes 表示在指定的節(jié)點上進行鏡像,節(jié)點名稱通過 ha-params 指定。
ha-params:ha-mode 模式需要用到的參數(shù)。
ha-sync-mode:進行隊列中消息的同步方式,有效值為 automatic 和 manual。
priority 為可選參數(shù),表示 policy 的優(yōu)先級。
配置完成后,點擊下面的 add/update policy 按鈕,完成策略的添加,如下:
添加完成后,我們可以進行一個簡單的測試。
首先確認三個 RabbitMQ 都啟動了,然后用上面的 provider 向消息隊列發(fā)送一條消息。
發(fā)完之后關(guān)閉 mq01 實例。
接下來啟動 consumer,此時發(fā)現(xiàn) consumer 可以完成消息的消費(注意和前面的反向測試區(qū)分),這就說明鏡像隊列已經(jīng)搭建成功了。
3.2 命令行配置鏡像隊列
命令行的配置格式如下:
rabbitmqctl set_policy [-p vhost] [--priority priority] [--apply-to apply-to] {name} {pattern} {definition}
1
舉一個簡單的配置案例:
rabbitmqctl set_policy -p / --apply-to queues my_queue_mirror "^" '{"ha-mode":"all","ha-sync-mode":"automatic"}'
1
4. 小結(jié)
好啦,這就是松哥和大家分享的 RabbitMQ 中的集群搭建,感興趣的小伙伴趕緊去試試吧~
Docker RabbitMQ
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。