0.前言
目前手上的一個(gè)中長(zhǎng)期任務(wù)就是消息隊(duì)列的升級(jí)咒吐,繼RocketMQ之后执庐,開(kāi)始對(duì)RabbitMQ進(jìn)行研究受楼,首先當(dāng)然還是簡(jiǎn)單的原理了解加集群環(huán)境搭建命迈。
1.集群原理
RabbitMQ 是一個(gè)由 Erlang 語(yǔ)言開(kāi)發(fā)的 AMQP (Advanced Message Queue,高級(jí)消息隊(duì)列協(xié)議)的開(kāi)源實(shí)現(xiàn)稳捆,因?yàn)镋rlang自帶集群特性赠法,因此RabbitMQ的集群也是基于此實(shí)現(xiàn)的。RabbitMQ的集群有兩種模式,分別為默認(rèn)集群模式和鏡像模式砖织。
-
默認(rèn)模式:消息實(shí)體只存在于一個(gè)節(jié)點(diǎn)上款侵,其它節(jié)點(diǎn)僅存儲(chǔ)對(duì)應(yīng)的元數(shù)據(jù),當(dāng)客戶(hù)端連接到未存儲(chǔ)消息實(shí)體的節(jié)點(diǎn)時(shí)侧纯,RabbitMQ會(huì)根據(jù)元數(shù)據(jù)從對(duì)應(yīng)節(jié)點(diǎn)取得消息后再轉(zhuǎn)發(fā)給客戶(hù)端新锈。這樣做有一個(gè)顯而易見(jiàn)的問(wèn)題是,當(dāng)某個(gè)節(jié)點(diǎn)宕機(jī)時(shí)眶熬,無(wú)論該節(jié)點(diǎn)是否有做消息持久化妹笆,存儲(chǔ)在該節(jié)點(diǎn)的未消費(fèi)消息都會(huì)暫時(shí)或者永久無(wú)法被消費(fèi)。
元數(shù)據(jù)復(fù)制.jpg 鏡像模式:鏡像模式顧名思義就是將一個(gè)節(jié)點(diǎn)存儲(chǔ)的消息在鏡像節(jié)點(diǎn)上進(jìn)行主動(dòng)同步娜氏,以防止單個(gè)節(jié)點(diǎn)宕機(jī)造成的問(wèn)題拳缠。當(dāng)然這種模式的消息高可靠性也是建立在性能損耗上的,適用于對(duì)消息可靠性要求特別高的場(chǎng)合贸弥,如訂單類(lèi)消息窟坐。
本文后面的講解都會(huì)基于默認(rèn)的集群模式來(lái)進(jìn)行。
1.1集群元數(shù)據(jù)的同步
當(dāng)在集群中聲明隊(duì)列绵疲、交換器哲鸳、綁定的時(shí)候,這些操作會(huì)直到所有集群節(jié)點(diǎn)都成功提交元數(shù)據(jù)變更后才返回盔憨。RabbitMQ集群會(huì)始終同步四種類(lèi)型的內(nèi)部元數(shù)據(jù)(類(lèi)似索引):
- a.隊(duì)列元數(shù)據(jù):隊(duì)列名稱(chēng)和它的屬性徙菠;
- b.交換器元數(shù)據(jù):交換器名稱(chēng)、類(lèi)型和屬性般渡;
- c.綁定元數(shù)據(jù):一張簡(jiǎn)單的表格展示了如何將消息路由到隊(duì)列懒豹;
- d.vhost元數(shù)據(jù):為vhost內(nèi)的隊(duì)列、交換器和綁定提供命名空間和安全屬性驯用;
因此脸秽,當(dāng)用戶(hù)訪問(wèn)其中任何一個(gè)RabbitMQ節(jié)點(diǎn)時(shí),通過(guò)rabbitmqctl查詢(xún)到的queue/user/exchange/vhost等信息都是相同的蝴乔。
1.2內(nèi)存節(jié)點(diǎn)和磁盤(pán)節(jié)點(diǎn)
集群中的節(jié)點(diǎn)有內(nèi)存節(jié)點(diǎn)和磁盤(pán)節(jié)點(diǎn)兩種類(lèi)型记餐,內(nèi)存節(jié)點(diǎn)雖然不寫(xiě)入磁盤(pán),但是它的執(zhí)行比磁盤(pán)節(jié)點(diǎn)要好薇正。內(nèi)存節(jié)點(diǎn)可以提供出色的性能片酝,磁盤(pán)節(jié)點(diǎn)能保障配置信息在節(jié)點(diǎn)重啟后仍然可用,那集群中如何平衡這兩者呢挖腰?
RabbitMQ 只要求集群中至少有一個(gè)磁盤(pán)節(jié)點(diǎn)雕沿,所有其他節(jié)點(diǎn)可以是內(nèi)存節(jié)點(diǎn),當(dāng)節(jié)點(diǎn)加入火離開(kāi)集群時(shí)猴仑,它們必須要將該變更通知到至少一個(gè)磁盤(pán)節(jié)點(diǎn)审轮。如果只有一個(gè)磁盤(pán)節(jié)點(diǎn),剛好又是該節(jié)點(diǎn)崩潰了,那么集群可以繼續(xù)路由消息疾渣,但不能創(chuàng)建隊(duì)列篡诽、創(chuàng)建交換器、創(chuàng)建綁定榴捡、添加用戶(hù)杈女、更改權(quán)限、添加或刪除集群節(jié)點(diǎn)吊圾。換句話說(shuō)集群中的唯一磁盤(pán)節(jié)點(diǎn)崩潰的話达椰,集群仍然可以運(yùn)行,但知道該節(jié)點(diǎn)恢復(fù)项乒,否則無(wú)法更改任何東西砰碴。
2.集群搭建
我的RabbitMQ集群搭建在兩臺(tái)centos6.9服務(wù)器上。這里我使用了最簡(jiǎn)單的通過(guò)yum方式安裝RabbitMQ板丽,由于RabbitMQ依賴(lài)于Erlang環(huán)境,通過(guò)yum的方式安裝會(huì)自動(dòng)處理依賴(lài)的程序趁尼,所以會(huì)自動(dòng)安裝好Erlang埃碱,對(duì)于不熟悉Erlang的人來(lái)說(shuō)省去了很多麻煩。
2.1單機(jī)安裝
首先看一下yum源中的RabbitMQ版本酥泞,通過(guò)下述命令查詢(xún)
yum list | grep rabbitmq
查到最新的安裝包為rabbitmq-server.noarch砚殿,版本為3.1.5-1.el6,雖然不是最新的芝囤,但暫時(shí)應(yīng)該夠用了似炎,通過(guò)以下命令安裝
yum -y install rabbitmq-server.noarch
安裝完成后首先通過(guò)erl -v
命令檢查Erlang環(huán)境是否已安裝
Erlang R14B04 (erts-5.8.5) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:0] [kernel-poll:false]
Eshell V5.8.5 (abort with ^G)
1>
可以看到R14B04版本已經(jīng)安裝完成,這也是目前yum源自帶的最高版本了悯姊,如果要安裝更高版本的RabbitMQ這個(gè)Erlang版本是肯定不夠的羡藐,就需要再另外自行安裝更高版本Erlang。這里順帶提一下悯许,要退出Erlang的控制臺(tái)需要輸入halt().
命令仆嗦,否則只能通過(guò)ctrl+z
強(qiáng)行退出。
2.2服務(wù)啟動(dòng)
首先打開(kāi)服務(wù)
chkconfig rabbitmq-server on
啟動(dòng)RabbitMQ
service rabbitmq-server start
查看狀態(tài)
rabbitmqctl status
看到如下信息則證明RabbitMQ已正常啟動(dòng)
Status of node rabbit@host05 ...
[{pid,8927},
{running_applications,[{rabbit,"RabbitMQ","3.1.5"},
{os_mon,"CPO CXC 138 46","2.2.7"},
{mnesia,"MNESIA CXC 138 12","4.5"},
{xmerl,"XML parser","1.2.10"},
{sasl,"SASL CXC 138 11","2.1.10"},
{stdlib,"ERTS CXC 138 10","1.17.5"},
{kernel,"ERTS CXC 138 10","2.14.5"}]},
{os,{unix,linux}},
{erlang_version,"Erlang R14B04 (erts-5.8.5) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:30] [kernel-poll:true]\n"},
{memory,[{total,27533368},
{connection_procs,2704},
{queue_procs,5408},
{plugins,0},
{other_proc,9049416},
{mnesia,61424},
{mgmt_db,0},
{msg_index,34160},
{other_ets,798568},
{binary,5312},
{code,14419209},
{atom,1355273},
{other_system,1801894}]},
{vm_memory_high_watermark,0.4},
{vm_memory_limit,1607422771},
{disk_free_limit,1000000000},
{disk_free,18049622016},
{file_descriptors,[{total_limit,924},
{total_used,3},
{sockets_limit,829},
{sockets_used,1}]},
{processes,[{limit,1048576},{used,124}]},
{run_queue,0},
{uptime,74460}]
...done.
2.3加入集群
首先有兩個(gè)點(diǎn)需要注意:
- RabbitMQ節(jié)點(diǎn)的命名一般是
rabbit@服務(wù)器host
先壕,因此需要在/etc/hosts文件中配置對(duì)應(yīng)的ip和hostname瘩扼,防止host無(wú)法解析的情況; - RabbitMQ集群是基于Erlang的集群的垃僚,因此需要保證Erlang節(jié)點(diǎn)間的互通集绰。Erlang的節(jié)點(diǎn)互通需要節(jié)點(diǎn)之間有相同的cookie,因此將節(jié)點(diǎn)2的
/var/lib/rabbitmq/.erlang.cookie
修改為和節(jié)點(diǎn)1一樣谆棺,同時(shí)為保證cookie文件的權(quán)限栽燕,需要執(zhí)行以下命令:
chown rabbitmq:rabbitmq /var/lib/rabbitmq/.erlang.cookie
chmod 400 /var/lib/rabbitmq/.erlang.cookie
接著將節(jié)點(diǎn)2加入到節(jié)點(diǎn)1的集群中,首先需要停止節(jié)點(diǎn)2上的應(yīng)用(不是停止Erlang節(jié)點(diǎn)),否則會(huì)報(bào)以下錯(cuò)誤
Clustering node rabbit@host05 with rabbit@host04 ...
Error: mnesia_unexpectedly_running
在節(jié)點(diǎn)2上運(yùn)行以下命令
rabbitmqctl stop_app
清楚節(jié)點(diǎn)2上的隊(duì)列信息
rabbitmqctl reset
加入集群
rabbitmqctl join_cluster rabbit@節(jié)點(diǎn)1
如果要以?xún)?nèi)存節(jié)點(diǎn)模式加入則執(zhí)行以下命令
rabbitmqctl join_cluster --ram rabbit@節(jié)點(diǎn)1
重新啟動(dòng)節(jié)點(diǎn)2上的應(yīng)用
rabbitmqctl start_app
查看集群狀態(tài)
rabbitmqctl cluster_status
看到如下顯示證明集群建立成功
Cluster status of node rabbit@host04 ...
[{nodes,[{disc,[rabbit@host04,rabbit@host05]}]},
{running_nodes,[rabbit@host05,rabbit@host04]},
{partitions,[]}]
...done.
3.管理頁(yè)面
3.1頁(yè)面啟動(dòng)
rabbitmq-plugins enable rabbitmq_management
RabbitMQ很貼心的自帶管理頁(yè)面纫谅,該頁(yè)面屬于一種插件功能炫贤,通過(guò)上述命令啟動(dòng),啟動(dòng)后默認(rèn)通過(guò)http://ip:15672
登陸訪問(wèn)
這里順帶提一下其它默認(rèn)的端口:
- 4369 (epmd)
- 25672 (Erlang distribution)
- 5672, 5671 (AMQP 0-9-1 without and with TLS)
- 15672 (if management plugin is enabled)
- 61613, 61614 (if STOMP is enabled)
- 1883, 8883 (if MQTT is enabled)
3.2創(chuàng)建賬號(hào)
rabbitmqctl add_user admin admin
登錄管理頁(yè)面需要?jiǎng)?chuàng)建賬號(hào)付秕,如上所示命令用于創(chuàng)建admin/admin賬號(hào)
3.3賦予角色
rabbitmqctl set_user_tags admin administrator
上述命令將administrator角色賦給admin用戶(hù)兰珍,至此就可以通過(guò)admin/admin賬號(hào)進(jìn)行登陸了
3.4其它命令
其它常用用戶(hù)管理命令如下:
- list_users,用戶(hù)列表
- add_user {username} {password}询吴,添加用戶(hù)
- delete_user {username}掠河,刪除用戶(hù)
- change_password {username} {newpassword},修改密碼
- clear_password {username}猛计,刪除密碼唠摹,密碼刪除后就不能訪問(wèn)了
- authenticate_user {username} {password},用戶(hù)認(rèn)證
- set_user_tags {username} {tag ...}奉瘤,為用戶(hù)設(shè)置角色勾拉,tag可以是0個(gè)、一個(gè)盗温、或多個(gè)
常用權(quán)限管理命令如下:
- list_vhosts [vhostinfoitem ...]藕赞,獲取vhosts列表
- add_vhost {vhost}, eg:rabbitmqctl add_vhost test
- delete_vhost {vhost}
- set_permissions [-p vhost] {user} {conf} {write} {read}卖局,給用戶(hù)分在對(duì)應(yīng)的vhost上分配相應(yīng)的權(quán)限斧蜕。eg:rabbitmqctl set_permissions -p /myvhost chris "^chris-." "." ".*",這條指令砚偶,給用戶(hù)chris在myvhost分配了權(quán)限批销,權(quán)限包括:以"chris-"開(kāi)頭的全部資源的配置權(quán)限,和所有資源的讀寫(xiě)權(quán)限
- clear_permissions [-p vhost] {username}染坯,清除權(quán)限
- list_permissions [-p vhost]均芽,vhost權(quán)限分配列表
- list_user_permissions {username},user權(quán)限列表
4.遇到的問(wèn)題
服務(wù)無(wú)法啟動(dòng)酒请,并報(bào)了如下的錯(cuò)誤:
BOOT FAILED
===========
Error description:
{error,{cannot_log_to_file,"/var/log/rabbitmq/rabbit@host04.log",
{error,eacces}}}
Log files (may contain more information):
/var/log/rabbitmq/rabbit@host04.log
/var/log/rabbitmq/rabbit@host04-sasl.log
Stack trace:
[{rabbit,ensure_working_log_handler,5},
{rabbit,ensure_working_log_handlers,0},
{rabbit,'-boot/0-fun-1-',0},
{rabbit,start_it,1},
{init,start_it,1},
{init,start_em,1}]
{"init terminating in do_boot",{rabbit,failure_during_boot,{error,{cannot_log_to_file,"/var/log/rabbitmq/rabbit@host04.log",{error,eacces}}}}}
Crash dump was written to: erl_crash.dump
init terminating in do_boot ()
查閱資料發(fā)現(xiàn)是rabbitmq沒(méi)有/var/log/rabbitmq目錄的權(quán)限造成的骡技,使用chown -R rabbitmq:rabbitmq /var/log/rabbitmq/
賦予權(quán)限后再次啟動(dòng),問(wèn)題解決羞反,但問(wèn)題造成的原因未知布朦。