一、Rabbitmq集羣部署手冊
1、環境介紹
系統環境:Red HatEnterprise Linux Server release 6.2 (Santiago)
內核版本:Linux zxt-02.com2.6.32-220.el6.x86_64 #1 SMP Wed Nov 9 08:03:13 EST 2011 x86_64 x86_64 x86_64GNU/Linux
軟件版本:otp_src_17.3;rabbitmq-server-3.2.4;Python 2.6.6;simplejson-3.3.2; haproxy-1.4.22-3.el6.x86_64.rpm
機器環境:
rabbit1 192.168.1.191
rabbit2 192.168.1.178
rabbit3 192.168.1.88
2、rabbitmq集羣簡介:
因爲公司架構需要,最近在學習研究mq集羣但是網上的各種文檔,都似是而非的感覺,最後無意間點擊到官網的安裝文檔然後就麼有然後了。。。。。如果英文水平較好的讀者本強烈建議略過本文檔直接點擊一下鏈接參考官方文檔。
http://www.rabbitmq.com/clustering.html#issues-hostname
http://www.rabbitmq.com/man/rabbitmqctl.1.man.html
http://www.rabbitmq.com/ha.html
RabbitMQ是用erlang開發的,集羣非常方便,因爲erlang天生就是一門分佈式語言,但其本身並不支持負載均衡。
Rabbit模式大概分爲以下三種:
單一模式、普通模式、鏡像模式
單一模式:最簡單的情況,非集羣模式。
沒什麼好說的。
普通模式:默認的集羣模式。
對於Queue來說,消息實體只存在於其中一個節點,A、B兩個節點僅有相同的元數據,即隊列結構。當消息進入A節點的Queue中後,consumer從B節點拉取時,RabbitMQ會臨時在A、B間進行消息傳輸,把A中的消息實體取出並經過B發送給consumer。所以consumer應儘量連接每一個節點,從中取消息。即對於同一個邏輯隊列,要在多個節點建立物理Queue。否則無論consumer連A或B,出口總在A,會產生瓶頸。該模式存在一個問題就是當A節點故障後,B節點無法取到A節點中還未消費的消息實體。
如果做了消息持久化,那麼得等A節點恢復,然後纔可被消費;如果沒有持久化的話,然後就沒有然後了……
鏡像模式:
把需要的隊列做成鏡像隊列,存在於多個節點,屬於RabbitMQ的HA方案。該模式解決了上述問題,其實質和普通模式不同之處在於,消息實體會主動在鏡像節點間同步,而不是在consumer取數據時臨時拉取。
該模式帶來的副作用也很明顯,除了降低系統性能外,如果鏡像隊列數量過多,加之大量的消息進入,集羣內部的網絡帶寬將會被這種同步通訊大大消耗掉。所以在對可靠性要求較高的場合中適用
瞭解集羣中的基本概念:
RabbitMQ的集羣節點包括內存節點、磁盤節點。顧名思義內存節點就是將所有數據放在內存,磁盤節點將數據放在磁盤。不過,如前文所述,如果在投遞消息時,打開了消息的持久化,那麼即使是內存節點,數據還是安全的放在磁盤。一個rabbitmq集羣中可以共享 user,vhost,queue,exchange等,所有的數據和狀態都是必須在所有節點上覆制的,一個例外是,那些當前只屬於創建它的節點的消息隊列,儘管它們可見且可被所有節點讀取。rabbitmq節點可以動態的加入到集羣中,一個節點它可以加入到集羣中,也可以從集羣環集羣會進行一個基本的負載均衡。
集羣中有兩種節點:
1 內存節點:只保存狀態到內存(一個例外的情況是:持久的queue的持久內容將被保存到disk)
2 磁盤節點:保存狀態到內存和磁盤。
內存節點雖然不寫入磁盤,但是它執行比磁盤節點要好。集羣中,只需要一個磁盤節點來保存狀態就足夠了
如果集羣中只有內存節點,那麼不能停止它們,否則所有的狀態,消息等都會丟失。
高可用解決思路:
那麼具體如何實現RabbitMQ高可用,我們先搭建一個普通集羣模式,在這個模式基礎上再配置鏡像模式實現高可用,Rabbit集羣前增加一個反向代理,消息的生產者、消費者都通過反向代理訪問RabbitMQ集羣。
此次因有配圖
3、RabbitMQ集羣配置
1) 安裝MQ
在本文檔案例中 rabbit1、rabbit2、rabbit3作爲rabbitmq的三個羣集節點分別用安裝RabbitMq-Server關於mq的安裝請參考:
請參考本人之前的博客安裝
(ps:據說配置外部yum源之後可以yum安裝erlang和mq等軟件)。
2) 配置服務器hosts文件
在安裝好的三臺節點服務器中分別修改/etc/hosts文件,確保三個節點能夠互相解析機器名。請確保hostname文件的機器名沒有錯誤。((注意:建議安裝rabbitmq之前配置hostane和hosts文件)。
[root@rabbit1 ~]# cat /etc/hosts 127.0.0.1 localhostlocalhost.localdomain localhost4 localhost4.localdomain4 ::1 localhostlocalhost.localdomain localhost6 localhost6.localdomain6 192.168.1.195 rabbit1 192.168.1.178 rabbit2 192.168.1.9 rabbit3
3) 配置erlang羣集
Rabbitmq的集羣是依賴於erlang的集羣來工作的,所以必須先構建起erlang的集羣環境。Erlang的集羣中各節點是通過一個magic cookie來實現的,這個cookie一般位於 /var/lib/rabbitmq/.erlang.cookie 或 $HOME /.erlang.cookie。文件是400的權限。所以必須保證各節點cookie保持一致,否則節點之間就無法通信。
注意:cookie所在的家目錄一般是啓動mq的目錄,如初次安裝啓動mq則不會生成該文件。
將其中一臺節點上的.erlang.cookie值複製下來保存到其他節點上。或者使用scp的方法也可,但是要注意文件的權限和屬主屬組。我們這裏將rabbit1中的cookie 複製到rabbit2、rabbit3中,
[tianyi@rabbit1 ~]$ pwd /home/tianyi [tianyi@rabbit1 ~]$ ll .erlang.cookie -r-------- 1 tianyi tianyi 20 Jan 8 00:00 .erlang.cookie
將rabbit1的/home/tianyi/.erlang.cookie這個文件,拷貝到rabbit2、rabbit3的同一位置(反過來亦可),該文件是集羣節點進行通信的驗證密鑰,所有節點必須一致。拷完後啓動RabbitMQ
[tianyi@rabbit1 ~]$ chmod 766 .erlang.cookie [tianyi@rabbit1 ~]$ scp .erlang.cookie 192.168.1.178:~ [tianyi@rabbit1 ~]$ scp .erlang.cookie 192.168.1.88:~ [tianyi@rabbit1 ~]$ chmod 400 .erlang.cookie [tianyi@rabbit2 ~]$ chmod 400 .erlang.cookie [tianyi@rabbit3 ~]$ chmod 400 .erlang.cookie
注意:
A、複製好後別忘記還原.erlang.cookie的權限,否則可能會遇到錯誤.
B、儘量不要在RabbitMQ的狀態下更改.erlang.cookie文件,否則執行”./rabbitmqctl sotp”關閉MQ時我出現錯誤。解決辦法是直接kill掉RabbitMQ進程重新啓動即可。
4) 啓動獨立節點
設置好cookie後先將三個節點的rabbitmq重啓
cd /opt/app/rabbitmq/sbin ##rabbitmq的安裝目錄 [tianyi@rabbit1 sbin]$ ./rabbitmq-server -detached [tianyi@rabbit2 sbin]$ ./rabbitmq-server-detached [tianyi@rabbit3 sbin]$ ./rabbitmq-server–detached
5) 查看單節點的集羣狀態
cd /opt/app/rabbitmq/sbin [tianyi@rabbit1 sbin]$ ./rabbitmqctlcluster_status Cluster status of node rabbit@rabbit1 ... [{nodes,[{disc,[rabbit@rabbit1]}]},{running_nodes,[rabbit@rabbit1]}] ...done. [tianyi@rabbit2 sbin]$ ./rabbitmqctlcluster_status Cluster status of node rabbit@rabbit2 ... [{nodes,[{disc,[rabbit@rabbit2]}]},{running_nodes,[rabbit@rabbit2]}] ...done. [tianyi@rabbit3 sbin]$ ./rabbitmqctlcluster_status Cluster status of node rabbit@rabbit3 ... [{nodes,[{disc,[rabbit@rabbit3]}]},{running_nodes,[rabbit@rabbit3]}] ...done.
6) 創建集羣
我們將 rabbit2 和rabbit3 與rabbit1組成一個集羣,我們第一次將rabbit2 與rabbit1連接。
注意:
要加入集羣必須先停止rabbit2的 RabbitMQ 應用程序,加入集羣后重啓RabbitMQ的應用程序。
[tianyi@rabbit2 sbin]$ ./rabbitmqctlstop_app Stopping node rabbit@rabbit2 ...done. [tianyi@rabbit2 sbin]$ ./rabbitmqctljoin_cluster rabbit@rabbit1 Clustering node rabbit@rabbit2 with [rabbit@rabbit1] ...done. [tianyi@rabbit2 sbin]$ ./rabbitmqctlstart_app Starting node rabbit@rabbit2 ...done.
我們可以看到,兩個節點連接在一個集羣中運行 cluster_status 命令查看節點集羣狀態:
[tianyi@rabbit1 sbin]$ ./rabbitmqctlcluster_status Cluster status of node rabbit@rabbit1 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]}, {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}] ...done. [tianyi@rabbit2 sbin]$ ./rabbitmqctlcluster_status Cluster status of node rabbit@rabbit2 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]}, {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}] ...done.
將rabbit3加入集羣上方已經將rabbit2與rabbit1連接,也可以直接將rabbit3與rabbit1連接,同樣可以加入集羣中。
[tianyi@rabbit3 sbin]$ ./rabbitmqctlstop_app Stopping node rabbit@rabbit3 ...done. [tianyi@rabbit3 sbin]$ ./rabbitmqctljoin_cluster rabbit@rabbit2 Clustering node rabbit@rabbit3 with rabbit@rabbit2 ...done. [tianyi@rabbit3 sbin]$ ./rabbitmqctlstart_app Starting node rabbit@rabbit3 ...done.
運行 cluster_status 命令,我們可以看到三個節點連接在一個集羣中:
[tianyi@rabbit1 sbin]$ ./rabbitmqctlcluster_status Cluster status of node rabbit@rabbit1 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit3,rabbit@rabbit2,rabbit@rabbit1]}] ...done. [tianyi@rabbit2 sbin]$ ./rabbitmqctlcluster_status Cluster status of node rabbit@rabbit2 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit3,rabbit@rabbit1,rabbit@rabbit2]}] ...done. [tianyi@rabbit3 sbin]$./rabbitmqctlcluster_status Cluster status of node rabbit@rabbit3 ... [{nodes,[{disc,[rabbit@rabbit3,rabbit@rabbit2,rabbit@rabbit1]}]}, {running_nodes,[rabbit@rabbit2,rabbit@rabbit1,rabbit@rabbit3]}] ...done.
如果要爲集羣增加新節點時,我們可以按照上面的步驟將新節點添加到集羣。
7) 停止一個節點
在集羣中如果要停止一個節點執行命令“rabbitmqctl stop_app”或“rabbitmqctl stop”或者可以kill掉節點的進程不影響其他節點運行(注意:如果只有一個磁盤節點,如果幹掉磁盤節點後消息數據會丟失)。
停止節點3
[tianyi@rabbit3 sbin]$ ./rabbitmqctlstop Stopping and halting node rabbit@rabbit3 ... ...done.
查看其他節點狀態:
[tianyi@rabbit1 sbin]$ ./rabbitmqctlcluster_status Cluster status of node rabbit@rabbit1 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}, {partitions,[]}] ...done. [tianyi@rabbit2 sbin]$ ./rabbitmqctlcluster_status Cluster status of node rabbit@rabbit2 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}, {partitions,[]}] ...done.
8) 分離一個節點
上面我沒已經操作過停止節點,爲啥還要講節點分離,集羣節點無論任何原因停止之後只要再次啓動rabbitmq此節點依然可以自動加集羣。
分離節點:將節點與集羣完全分離,即時再啓動rabbitmq進程依然無法加入集羣。
[tianyi@rabbit3 sbin]$ ./rabbitmqctlstop_app Stopping node rabbit@rabbit3 ...done. [tianyi@rabbit3 sbin]$ ./rabbitmqctlreset //重置節點 Resetting node rabbit@rabbit3 ...done. [tianyi@rabbit3 sbin]$ ./rabbitmqctlstart_app Starting node rabbit@rabbit3 ...done.
查看分離後的集羣狀態:
[tianyi@rabbit3 sbin]$ ./rabbitmqctlcluster_status //rabbit3處於獨立狀態 Cluster status of node rabbit@rabbit3 ... [{nodes,[{disc,[rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit3]}, {partitions,[]}] ...done. //rabbit1和rabbit2依然是集羣。 [tianyi@rabbit1 sbin]$./rabbitmqctl cluster_status Cluster status of node rabbit@rabbit1 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]}, {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}, {partitions,[]}] ...done. [tianyi@rabbit2 sbin]$ ./rabbitmqctlcluster_status Cluster status of node rabbit@rabbit2 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]}, {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}, {partitions,[]}] ...done.
9) 創建內存節點集羣
創建內存節點與創建磁盤節點的命令只多了一條“--ram”參數而已,如果需要創建內存節點方法如下。
(如果集羣機器較多條件允許的話建議使用內存節點和磁盤節點混搭的架構速度和安全性都會有一定的提升)
[tianyi@rabbit3 sbin]$./rabbitmqctlstop_app Stopping node rabbit@rabbit3 ...done. [tianyi@rabbit3 sbin]$./rabbitmqctljoin_cluster --ram rabbit@rabbit2 Clustering node rabbit@rabbit3 with rabbit@rabbit2 ...done. [tianyi@rabbit3 sbin]$ ./rabbitmqctlstart_app Starting node rabbit@rabbit3 ...done. RAM節點集羣中顯示爲這樣的狀態: [tianyi@rabbit1 sbin]$ ./rabbitmqctlcluster_status Cluster status of node rabbit@rabbit1 ... [{nodes,[{disc,[rabbit@rabbit1]},{ram,[rabbit@rabbit2]}]}, {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}] ...done. [tianyi@rabbit2 sbin]$ ./rabbitmqctlcluster_status Cluster status of node rabbit@rabbit2 ... [{nodes,[{disc,[rabbit@rabbit1]},{ram,[rabbit@rabbit2]}]}, {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}] ...done.
10) 改變節點類型
我們可以改變節點的類型從ram盤和副。 說我們想要扭轉的類型rabbit@rabbit2 和 rabbit@rabbit1 ,把前者從ram節點到盤節點,後者從盤節點到ram節點。要做到這一點,我們可以使用 change_cluster_node_type 命令。 節點必須 先停止
將Rabbit3改爲磁盤節點:
[tianyi@rabbit3 sbin]$./rabbitmqctl stop_app Stopping node rabbit@rabbit3 ... ...done. [tianyi@rabbit3 sbin]$ ./rabbitmqctlchange_cluster_node_type disc Turning rabbit@rabbit3 into a disc node ... ...done. [tianyi@rabbit3 sbin]$ ./rabbitmqctlstart_app Starting node rabbit@rabbit3 ... ...done.
將Rabbit3改爲內存節點:
[tianyi@rabbit1 sbin]$ ./rabbitmqctlstop_app Stopping node rabbit@rabbit1 ... ...done. [tianyi@rabbit1 sbin]$ ./rabbitmqctlchange_cluster_node_type ram Turning rabbit@rabbit1 into a ram node ... ...done. [tianyi@rabbit1 sbin]$./rabbitmqctl start_app Starting node rabbit@rabbit1 ... ...done.
4、配置rabbitmq鏡像模式
http://www.rabbitmq.com/man/rabbitmqctl.1.man.html
http://www.rabbitmq.com/ha.html
上面配置的是RabbitMQ的默認集羣模式,並不保證隊列的高可用性,儘管交換機、綁定這些可以複製到集羣裏的任何一個節點,但是隊列內容不會複製,雖然該模式解決一部分節點壓力,但隊列節點宕機直接導致該隊列無法使用,只能等待重啓,所以要想在隊列節點宕機或故障也能正常使用,就要複製隊列內容到集羣裏的每個節點,需要創建鏡像隊列。
使用Rabbit鏡像功能,需要基於rabbitmq策略來實現,政策是用來控制和修改羣集範圍的某個vhost隊列行爲和Exchange行爲 在集羣中的任意節點啓用策略,策略會自動同步到集羣節點
[tianyi@rabbit1 sbin]$rabbitmqctl set_policy ha-all"^" '{"ha-mode":"all"}'
//在任意節點執行上面的這條命令即可將普通集羣設置爲鏡象集羣。
這行命令策略模式爲 all 即複製到所有節點,包含新增節點,策略正則表達式爲 “^” 表示所有匹配所有隊列名稱。
例如rabbitmqctl set_policy -p hrsystem ha-allqueue "^message" '{"ha-mode":"all"}'
注意:"^message" 這個規則要根據自己修改,這個是指同步"message"開頭的隊列名稱,我們配置時使用的應用於所有隊列,所以表達式爲"^"
官方set_policy說明參見
set_policy [-p vhostpath] {name} {pattern}{definition} [priority] (http://www.rabbitmq.com/man/rabbitmqctl.1.man.html)
5、配置ha代理:
將RabbitMQ集羣設置爲鏡像模式之後,需要用負載均衡服務將訪問壓力分散於集羣中的每個節點,在此我們選擇了HAProxy,(當然根據實際需求也可以選擇lvs、nginx等)它的特點在於配置簡單,並且功能十分強大,配置過程歸納起來也就是安裝、設置配置文件、啓動服務這三步這麼簡單。在生產環境應該有單獨一臺服務器來運行負載均衡服務,我們這裏因測試條件有限本例中我們使用rabbit3運行負載均衡服務
5.1、安裝HAProxy
[root@rabbit3 soft]# rpm -ivh haproxy-1.4.22-3.el6.x86_64.rpm warning: haproxy-1.4.22-3.el6.x86_64.rpm: Header V3 RSA/SHA1Signature, key ID c105b9de: NOKEY Preparing... ########################################### [100%] 1:haproxy ########################################### [100%]
5.2、配置HAProxy
關於HAProxy的配置因爲本文主要內容不是介紹HAProxy因此此次只貼出配置文件供參考
global log 127.0.0.1 local0 #使用tcp監聽模式 mode tcp listen admin_stat #haproxy的web管理端口 8888,自行設置 bind 0.0.0.0:8888 mode http stats refresh 30s #haproxy web管理url,自行設置 listen rabbitmq 0.0.0.0:5670
#監聽5670端口,並轉發給三個節點的5672端口,採用輪詢策略
mode tcp balance roundrobin server rabbitmq-1 192.168.64.87:5672 check inter 2000 rise 2fall 3 server rabbitmq-2 192.168.64.88:5672 check inter 2000 rise 2fall 3 server rabbitmq-3 192.168.64.89:5672 check inter 2000 rise 2fall 3
5.3、啓用HAProxy
[root@rabbit3 haproxy]# /etc/init.d/haproxy start Or /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg –D
附件:參考文獻:
http://www.cnblogs.com/flat_peach/archive/2013/04/07/3004008.html
http://blog.csdn.net/tantexian/article/details/44806313
http://www.rabbitmq.com/ha.html