一、集羣結構
RabbitMQ會始終記錄以下四種類型的內部元數據:
隊列元數據:隊列的名稱、屬性(可持久化,能否自動刪除);
交換器元數據:交換器名稱、類型和屬性(可持久化等);
綁定元數據:一張記錄瞭如何將消息路由到隊列的表;
vhost元數據:爲vhost內的隊列、交換器和綁定提供命名空間和安全屬性;
在單一節點內,RabbitMQ會將這些信息存儲在內存中,同時將那些標記爲可持久化的隊列和交換器(以及綁定)存儲到硬盤上。
1.1、集羣中的隊列
默認情況下,當在集羣中創建隊列時,集羣只會在單個節點上創建完整的隊列信息(元數據、狀態、內容),結果是隻有隊列的所有者節點知道有關隊列的所有信息,所有其他非所有者節點只知道隊列的元數據和指向該隊列存在的那個節點的指針。因此當集羣中某個節點崩潰時,該節點上的隊列和關聯的綁定都消失了。直到崩潰的節點恢復前,消費者都無法消費該隊列上的消息了。
爲什麼默認情況下RabbitMQ不將隊列內容和狀態複製到所有節點上呢?
存儲空間
性能。對於持久化消息來說,每一條消息都會觸發磁盤I/O,每次新增節點時,網絡和磁盤負載都會增加。
1.2、集羣中的交換器
交換器實際上只是一個名稱和一個隊列綁定列表。當將消息發佈到交換器時,實際上是由連接的信道將消息上的路由鍵跟交換器的綁定列表進行匹配,然後將消息路由到相應的隊列。
由於交換器只不過是一張查詢表,所以將交換器在整個集羣中複製起來很簡單,這樣集羣中的每個節點都擁有每個交換器的所有信息。
1.3、內存節點和磁盤節點
集羣中的RabbitMQ節點,分爲內存節點(RAM node)和磁盤節點(disk node)。內存節點將所有元數據、用戶、權限等存儲在內存中,磁盤節點將元數據存儲在磁盤上。對於單節點系統,只有磁盤節點,否則重啓RabbitMQ,所有關於系統的配置信息都會消失。
內存節點能保證集羣的性能,磁盤節點保證集羣配置信息不會因爲重啓而丟失。RabbitMQ要求在整個集羣中最少有一個磁盤節點,其他節點都可以是內存節點。
1.4、鏡像隊列
默認情況下,隊列只存在於集羣中的一個節點上。RabbitMQ採用“鏡像隊列”選項,將隊列拷貝到其他節點上,一旦主隊列不可用,消費者可以連接到其他節點上的隊列繼續工作。
要使用鏡像隊列,需要聲明一個Policy,該Policy可以匹配合適的隊列作爲鏡像隊列。
二、配置集羣
2.1、準備環境
主機名 | IP地址 | 操作系統 | 節點類型 |
master | 192.168.0.201 | CentOS 7.7 | 磁盤節點 |
slave1 | 192.168.0.202 | CentOS 7.7 | 內存節點 |
slave2 | 192.168.0.203 | CentOS 7.8 | 內存節點 |
2.2、配置/etc/hosts文件(三個節點上都要操作)
[root@master ~]# cat /etc/hosts …… 192.168.0.201 master 192.168.0.202 slave1 192.168.0.203 slave2
2.3、安裝RabbitMQ(三個節點上都要操作)
2.3.1、配置repo
[root@master ~]# cat /etc/yum.repos.d/erlang_solutions.repo [erlang-solutions] name=Centos $releasever - $basearch - Erlang Solutions baseurl=http://packages.erlang-solutions.com/rpm/centos/$releasever/$basearch gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/erlang_solutions.asc enabled=1 [root@master ~]# cat /etc/yum.repos.d/rabbitmq.repo [rabbitmq_rabbitmq-server] name=rabbitmq_rabbitmq-server baseurl=https://packagecloud.io/rabbitmq/rabbitmq-server/el/7/$basearch repo_gpgcheck=1 gpgcheck=0 enabled=1 gpgkey=https://packagecloud.io/rabbitmq/rabbitmq-server/gpgkey sslverify=1 sslcacert=/etc/pki/tls/certs/ca-bundle.crt metadata_expire=300 [root@master ~]# wget -O /etc/pki/rpm-gpg/erlang_solutions.asc https://packages.erlang-solutions.com/ubuntu/erlang_solutions.asc [root@master ~]# yum clean all [root@master ~]# yum makecache [root@master ~]# yum -y install erlang [root@master ~]# yum -y install rabbitmq-server
2.3.2、獲取配置文件
從GitHub上獲取模板文件:https://github.com/rabbitmq/rabbitmq-server/blob/master/docs/rabbitmq.conf.example
將配置文件命名爲:/etc/rabbitmq/rabbitmq.conf
2.4、在master上啓動RabbitMQ(在master上操作)
先添加Web管理插件(警告:在生產環境使用Web管理插件是不安全的):
[root@master ~]# rabbitmq-plugins list | grep management [root@master ~]# rabbitmq-plugins enable rabbitmq_management
啓動RabbitMQ:
[root@master ~]# rabbitmq-server -detached
在master上查看端口:
[root@master ~]# netstat -tlnp tcp 0 0 0.0.0.0:4369 0.0.0.0:* LISTEN 34678/epmd tcp 0 0 0.0.0.0:15672 0.0.0.0:* LISTEN 41444/beam.smp tcp 0 0 0.0.0.0:25672 0.0.0.0:* LISTEN 41444/beam.smp tcp6 0 0 :::5672 :::* LISTEN 41444/beam.smp
會發現四個新的端口(4369、25672、5672、15672):
4369被empd進程使用,全稱爲Erlang Port Mapper Daemon。當啓動一個分佈式Erlang節點時,它會用empd進程進行註冊,提供OS內核分配的地址和端口;
25672就是由OS分配給運行RabbitMQ的Erlang VM的;
5672則是AMQP客戶端連接RabbitMQ的端口;
15672是Web管理插件啓用的端口。
2.5、同步.erlang.cookie(在master上操作)
Erlang節點通過匹配Erlang cookie獲得認證。當創建RabbitMQ集羣時,需要保證各節點的Erlang cookie一致,才能讓節點之間互相通信。cookie一般存放在“~/.erlang.cookie”文件中,“~”通常指rabbitmq用戶的家目錄。
[root@master ~]# scp /var/lib/rabbitmq/.erlang.cookie root@slave1:/var/lib/rabbitmq/ [root@master ~]# scp /var/lib/rabbitmq/.erlang.cookie root@slave2:/var/lib/rabbitmq/
2.6、將slave1和slave2加入集羣,並作爲內存節點啓動(在slave1和slave2上操作)
[root@slave1 ~]# rabbitmq-plugins enable rabbitmq_management [root@slave1 ~]# rabbitmq-server -detached [root@slave1 ~]# rabbitmqctl stop_app [root@slave1 ~]# rabbitmqctl reset [root@slave1 ~]# rabbitmqctl join_cluster --ram rabbit@master [root@slave1 ~]# rabbitmqctl start_app
2.7、查看集羣狀態(任意節點都可執行)
現在來查看一下集羣狀態(圖片只顯示了部分內容):
2.8、改變節點類型
如果想將slave2改變爲磁盤節點,可以進行以下操作:
[root@slave2 ~]# rabbitmqctl stop_app [root@slave2 ~]# rabbitmqctl change_cluster_node_type disc Turning rabbit@slave2 into a disc node [root@slave2 ~]# rabbitmqctl start_app
2.9、移除集羣中的節點
如果想將slave2從集羣中移除,進行以下操作:
[root@slave2 ~]# rabbitmqctl stop_app [root@slave2 ~]# rabbitmqctl reset [root@slave2 ~]# rabbitmqctl start_app
操作完記得把slave2再加回集羣,方便進行以下的步驟。
三、鏡像隊列
創建一個RabbitMQ集羣,就這樣簡單。前面提到(1.4節所述),在默認情況下,隊列只存在於集羣中的一個節點上。那如何保證當隊列存在的節點崩潰時,消費者仍然可以消費該隊列中的消息呢?這就是鏡像隊列的功能了。
關於鏡像隊列,移步到Web管理控制檯上去看看。
3.1、創建一個超級權限用戶
當啓用Web管理插件時,有一個超級權限用戶“guest”,密碼爲“guest”。但是“guest”用戶默認只允許本地登錄。
爲了使用Web插件,臨時創建一個有超級權限的用戶(該用戶將會同步到集羣中的所有節點):
[root@master ~]# rabbitmqctl add_user admin admin [root@master ~]# rabbitmqctl set_user_tags admin administrator #爲admin添加管理員權限 Setting tags for user "admin" to [administrator] ... [root@master ~]# rabbitmqctl set_permissions admin ".*" ".*" ".*" #授予admin在默認vhost“/”上的配置、寫、讀權限 Setting permissions for user "admin" in vhost "/" ... [root@master ~]# rabbitmqctl list_users Listing users ... user tags admin [administrator] guest [administrator]
通過瀏覽器訪問管理控制檯: http://192.168.0.201:15672/
3.2、創建Policy:
依次點擊:頂部“Admin” -> 右側“Policies” -> 中間“Add/update a policy”。
填寫信息:
Name: queue_ha Pattern: ^q # 表示匹配所有以“q”開頭的隊列和交換器。使用“^.”將應用於所有隊列和交換器; Apply to:Exchanges and queues # 將規則用於所有匹配的交換器和隊列; Priority: 0 # 優先級; Definition: ha-mode = all # ha-mode有三個選項,all表示同步到所有節點,exactly和nodes需要配合ha-params選擇同步的節點個數; ha-sync-mode = automatic # 節點重啓後,鏡像隊列的同步策略。automatic爲自動同步,manual需要在節點上手動執行rabbitmqctl sync_queue [Queue name]來完成;
Policy創建成功後,登錄另外兩個節點的Web管理控制檯,都能看到該“Policy”。如下圖:
3.3、創建隊列
創建兩個隊列,一個以“q”開頭的“q1”隊列(因爲之前創建的Policy:“queue_ha”匹配以“q”開頭的隊列),一個以其他字母開頭的“tq2”隊列。
依次點擊:頂部“Queues” -> 中間“Add a new queue”。
3.3.1、創建“q1”隊列
填寫信息:
Type: Classic Name: q1 Durability: Durable # 聲明隊列爲持久化隊列。記住消息持久化規則:Persistent類型的消息到達持久化隊列; Node: rabbit@master # 隊列的主節點; Auto delete: No # 如果設置爲“Yes”,當有一個消費者訂閱到此隊列時,將不允許其他消費者訂閱;
將光標放在藍色的“+2”處,顯示已經同步到另外兩個節點了,並且已經應用了“queue_ha”Policy,且隊列爲“idle”可用狀態。如下圖:
3.3.2、創建“tq2”隊列
步驟與3.3.1相同。
3.4、往兩個隊列中發佈消息
3.4.1、往“q1”隊列中發佈消息
依次點擊:頂部“Queues” -> “All queues”中點擊“Name”爲“q1”的隊列 -> 下拉頁面在左側找到“Publish message”:
填寫信息:
Delivery mode: 2-Persistent # 聲明該消息爲可持久化的消息 Headers: q1_message = m1 # 可以設置爲任意字符串 Propeties: message_id = 1 # 設置屬性 Payload: “Hello RabbitMQ” # 消息的有效載荷,即消息的內容
點擊“Publish message”,頁面中央出現“Message published”提示,如下圖:
3.4.2、往“tq2”隊列中發佈消息
依次點擊:頂部“Queues” -> “All queues”中點擊“Name”爲“tq2”的隊列 -> 下拉頁面在左側找到“Publish message”:
3.5、模擬故障:
現在,已經存在兩個隊列了。一個爲鏡像隊列“q1”,並且同步到其餘兩個節點;一個普通隊列“tq2”,只存在於master節點上。並且兩個隊列中都發布了消息,如下圖:
3.5.1、模擬master節點故障(在master節點上執行):
[root@master ~]# rabbitmqctl shutdown Shutting down RabbitMQ node rabbit@master running at PID 14879 Waiting for PID 14879 to terminate RabbitMQ node rabbit@master running at PID 14879 successfully shut down
關掉master上的RabbitMQ之後,移步到slave1的Web管理控制檯: http://192.168.0.202:15672/。使用admin用戶(密碼爲admin)登錄後,查看隊列狀態,如下圖:
先來看看“q1”隊列,“Node”處的藍色標記由“+2”變成了“+1”,並且其他狀態並未改變;
再來看看“tq2”隊列,“State”變爲“down”的狀態,“Messages”的狀態也由原來的數字變成了“NaN”。
此時點擊“tq2”,彈出了“Not found”頁面,而“q1”隊列正常。如下圖:
正如在1.1.1節所述,集羣中普通隊列的非所有者節點只保留了該隊列的元數據,卻無法消費該隊列中的消息了。
3.5.2、模擬master節點恢復重啓
在master節點上執行:
[root@master ~]# rabbitmq-server -detached
再到Web管理控制檯上查看,“q1”處顯示“鏡像隊列在master上未同步”,如下圖:
因爲在3.2節,創建Policy時指定了鏡像隊列在節點重啓之後自動同步;“tq2”爲持久化隊列,並且隊裏中的一條持久化的消息,所以一段時間後,兩個隊列的狀態都恢復到master節點關閉之前的狀態了,如下圖:
四、爲Rabbit做負載均衡
到現在爲止,已經有了三個RabbitMQ節點,並且已經將重要的隊列設置爲鏡像隊列。但是客戶端不得不手工指定訪問單個節點,並且當單個節點故障時,不得不手工嘗試其他節點。是時候爲Rabbit集羣設置負載均衡了。
Rabbit一般採用HAProxy做負載均衡。
4.1、安裝HAProxy
在master節點上操作:
[root@master ~]# yum -y install haproxy
4.2、配置HAProxy
[root@master ~]# cat /etc/haproxy/haproxy.cfg global log 127.0.0.1 local2 chroot /var/lib/haproxy pidfile /var/run/haproxy.pid maxconn 4000 user haproxy group haproxy daemon stats socket /var/lib/haproxy/stats defaults mode tcp log global option tcplog option dontlognull retries 3 timeout connect 10s timeout client 1m timeout server 1m maxconn 3000 listen rabbitmq_local_cluster 192.168.0.201:5670 #前端IP,供生產者和消費者使用 mode tcp balance roundrobin #負載均衡選項 server rabbit 192.168.0.201:5672 check inter 5000 rise 2 fall 3 #負載均衡中的集羣節點配置 server rabbit 192.168.0.202:5672 check inter 5000 rise 2 fall 3 server rabbit 192.168.0.203:5672 check inter 5000 rise 2 fall 3 listen private_monitoring :8000 #數據統計頁面 mode http option httplog stats enable stats uri /stats stats refresh 5s
4.4、啓動HAProxy:
[root@master ~]# systemctl start haproxy
4.4、通過HAProxy數據統計頁面查看狀態
更多:
RabbitMQ學習之一:理解RabbitMQ
https://blog.51cto.com/13568014/2495857
RabbitMQ學習之二:管理單節點RabbitMQ
https://blog.51cto.com/13568014/2496390