一文帶你入門RabbitMQ消息隊列
基礎(chǔ)概念
什么是消息隊列
消息隊列是一種用于在應(yīng)用程序之間傳遞消息的通信方式,消息隊列允許應(yīng)用程序異步的發(fā)送和接收消息,并且不需要直接連接到對方。
消息指的是兩個應(yīng)用間傳遞的數(shù)據(jù)。數(shù)據(jù)的類型有很多種形式,可能只包含文本字符串,也可能包含嵌入對象。
隊列指的是存儲數(shù)據(jù)的介質(zhì),遵循先進(jìn)先出的規(guī)則。
圖片
為什么使用消息隊列
解耦
在傳統(tǒng)模式下,假設(shè)新增物流系統(tǒng)模塊,則需要訂單系統(tǒng)新增調(diào)用物流系統(tǒng)的代碼邏輯。假設(shè)庫存系統(tǒng)異常,訂單系統(tǒng)調(diào)用出錯,會導(dǎo)致整個服務(wù)不可用。為了降低這種強(qiáng)耦合,就可以使用MQ,系統(tǒng)訂單系統(tǒng)只需要把數(shù)據(jù)發(fā)送到MQ,其他系統(tǒng)如果需要數(shù)據(jù),則從MQ中獲取即可。
- 傳統(tǒng)模式
圖片
畫板
- 消息隊列模式
畫板
異步
如圖所示。進(jìn)行用戶注冊時,會進(jìn)行寫入數(shù)據(jù)庫、發(fā)送郵件、發(fā)送短信操作。同步請求的話,響應(yīng)時間就是所有操作步驟時間的總和,也就是1s。如果使用MQ,用戶模塊發(fā)送數(shù)據(jù)到MQ,然后就可以返回響應(yīng)給客戶端,不需要再等待系統(tǒng)其他操作的響應(yīng),可以大大地提高性能。對于一些非必要的業(yè)務(wù),比如發(fā)送短信,發(fā)送郵件等等,就可以采用MQ。
- 傳統(tǒng)模式
畫板
- 消息隊列模式
畫板
削峰
假設(shè)系統(tǒng)A在某一段時間請求數(shù)暴增,有5000個請求發(fā)送過來,系統(tǒng)A這時就會發(fā)送5000條SQL進(jìn)入MySQL進(jìn)行執(zhí)行,MySQL對于如此龐大的請求當(dāng)然處理不過來,MySQL就會崩潰,導(dǎo)致系統(tǒng)癱瘓。如果使用MQ,系統(tǒng)A不再是直接發(fā)送SQL到數(shù)據(jù)庫,而是把數(shù)據(jù)發(fā)送到MQ,MQ短時間積壓數(shù)據(jù)是可以接受的,然后由消費者每次拉取2000條進(jìn)行處理,防止在請求峰值時期大量的請求直接發(fā)送到MySQL導(dǎo)致系統(tǒng)崩潰。
- 傳統(tǒng)模式
畫板
- 消息隊列模式
畫板
RabbitMQ的特點
RabbitMQ是一款使用Erlang語言開發(fā)的,實現(xiàn)AMQP(高級消息隊列協(xié)議)的開源消息中間件。
- 可靠性。支持持久化,傳輸確認(rèn),發(fā)布確認(rèn)等保證了MQ的可靠性。
- 靈活的分發(fā)消息策略。這應(yīng)該是RabbitMQ的一大特點。在消息進(jìn)入MQ前由Exchange(交換機(jī))進(jìn)行路由消息。分發(fā)消息策略有:簡單模式、工作隊列模式、發(fā)布訂閱模式、路由模式、通配符模式。
- 支持集群。多臺RabbitMQ服務(wù)器可以組成一個集群,形成一個邏輯Broker。
- 多種協(xié)議。RabbitMQ支持多種消息隊列協(xié)議,比如 STOMP、MQTT 等等。
- 支持多種語言客戶端。RabbitMQ幾乎支持所有常用編程語言,包括 Java、.NET、Ruby 等等。
- 可視化管理界面。RabbitMQ提供了一個易用的用戶界面,使得用戶可以監(jiān)控和管理消息 Broker。
- 插件機(jī)制。RabbitMQ提供了許多插件,可以通過插件進(jìn)行擴(kuò)展,也可以編寫自己的插件。
消息隊列中間件對比
目前在市面上比較主流的MQ中間件主要有,ActiveMQ、RabbitMQ、Kafka、RocketMQ 等這幾種,對比如下表:
特性 | ActiveMQ | RabbitMQ | Kafka | RocketMQ |
所屬社區(qū)/公司 | Apache | Mozilla Public License | Apache | Apache/Ali |
單機(jī)呑吐量 | 萬級 | 萬級 | 幾十萬級 | 十萬級 |
時效性 | 毫秒級 | 微秒級 | 毫秒級 | 毫秒級 |
可用性 | 主從 | 主從 | 分布式 | 分布式 |
功能特性 | MQ領(lǐng)域功能極其完備 | 基于erlang開發(fā),所以并發(fā)能力很強(qiáng),性能極其好,延時很低 | 功能較為簡單,主要支持簡單的MQ功能,在大數(shù)據(jù)領(lǐng)域的實時計算以及日志采集被大規(guī)模使用 | MQ功能比較完備,擴(kuò)展性佳 |
消息可靠性 | 有較低的概率丟失數(shù)據(jù) | 基本不丟 | 經(jīng)過參數(shù)優(yōu)化配置,可以做到 0 丟失 | 同 Kafka |
事務(wù) | 支持 | 不支持 | 支持 | 支持 |
broker端消息過濾 | 支持 | 不支持 | 不支持 | 可以支持Tag標(biāo)簽過濾和SQL表達(dá)式過濾 |
消息查詢 | 支持 | 根據(jù)消息id查詢 | 不支持 | 支持Message id或Key查詢 |
消息回溯 | 支持 | 不支持 | 理論上可以支持時間或offset回溯,但是得修改代碼。 | 支持按時間來回溯消息,精度毫秒,例如從一天之前的某時某分某秒開始重新消費消息。 |
路由邏輯 | 無 | 基于交換機(jī),可配置復(fù)雜路由邏輯 | 根據(jù)topic | 根據(jù)topic,可以配置過濾消費 |
持久化 | 內(nèi)存、文件、數(shù)據(jù)庫 | 隊列基于內(nèi)存,只能少量堆積 | 磁盤,大量堆積 | 磁盤,大量堆積 |
順序消息 | 支持 | 不支持 | 支持 | 支持 |
社區(qū)活躍度 | 低 | 中 | 高 | 高 |
適用場景 | 解耦和異步調(diào)用,較少在大規(guī)模吞吐的場景中使用 | 數(shù)據(jù)量小對時效性要求高的場景中使用 | 大數(shù)據(jù)類的系統(tǒng)來進(jìn)行實時數(shù)據(jù)計算、日志采集等場景。 | 目前在阿里被廣泛應(yīng)用在訂單、交易、充值、流計算、消息推送、日志流式處理、binglog分發(fā)消息等場景。 |
RabbitMQ原理
內(nèi)部結(jié)構(gòu)
RabbitMQ 本質(zhì)是 AMQP 協(xié)議的一個開源實現(xiàn),在詳細(xì)介紹 RabbitMQ 之前,我們先來看一下 AMQP 的內(nèi)部結(jié)構(gòu)圖
圖片
- Publisher:消息的生產(chǎn)者,也是一個向交換器發(fā)布消息的客戶端應(yīng)用程序
- Exchange:交換器,用來接收生產(chǎn)者發(fā)送的消息并將這些消息路由給服務(wù)器中的隊列
- Binding:綁定,用于將消息隊列和交換器之間建立關(guān)聯(lián)。一個綁定就是基于路由鍵將交換器和消息隊列連接起來的路由規(guī)則,所以可以將它理解成一個由綁定構(gòu)成的路由表。
- Queue:消息隊列,用來保存消息直到發(fā)送給消費者
- Connection:網(wǎng)絡(luò)連接,比如一個 TCP 連接
- Channel:信道,多路復(fù)用連接中的一條獨立的雙向數(shù)據(jù)流通道
- Consumer:消息的消費者,表示一個從消息隊列中取得消息的客戶端應(yīng)用程序
- Virtual Host:虛擬主機(jī),表示一批交換器、消息隊列和相關(guān)對象。虛擬主機(jī)是共享相同的身份認(rèn)證和加密環(huán)境的獨立服務(wù)器域。每個 vhost 本質(zhì)上就是一個 mini 版的 RabbitMQ 服務(wù)器,擁有自己的隊列、交換器、綁定和權(quán)限機(jī)制。vhost 是 AMQP 概念的基礎(chǔ),必須在連接時指定,RabbitMQ 默認(rèn)的 vhost 是 /
- Broker:表示消息隊列服務(wù)器實體
- Message:消息實體,它由消息頭和消息體組成。消息頭主要由路由鍵、交換器、隊列、priority(相對于其他消息的優(yōu)先權(quán))、delivery-mode(指出該消息可能需要持久性存儲)等屬性組成,而消息體就是指具體的業(yè)務(wù)對象。
相比傳統(tǒng)的 JMS 模型,AMQP 主要多了 Exchange、Binding 這個新概念。
在 AMQP 模型中,消息的生產(chǎn)者不是直接將消息發(fā)送到Queue隊列,而是將消息發(fā)送到Exchange交換器,其中還新加了一個中間層Binding綁定,作用就是通過路由鍵Key將交換器和隊列建立綁定關(guān)系。
圖片
就好比類似**用戶表和角色表,中間通過用戶角色表**來將用戶和角色建立關(guān)系,從而實現(xiàn)關(guān)系綁定,在 RabbitMQ 中,消息生產(chǎn)者不直接跟隊列建立關(guān)系,而是將消息發(fā)送到交換器之后,由交換器通過已經(jīng)建立好的綁定關(guān)系,將消息發(fā)送到對應(yīng)的隊列!
RabbitMQ 最終的架構(gòu)模型,核心部分就變成如下圖所示:
圖片
從圖中很容易看出,與 JMS 模型最明顯的差別就是消息的生產(chǎn)者不直接將消息發(fā)送給隊列,而是由Binding綁定決定交換器的消息應(yīng)該發(fā)送到哪個隊列,進(jìn)一步實現(xiàn)了在消息的推送方面,更加靈活!
交換器分發(fā)策略
當(dāng)消息的生產(chǎn)者將消息發(fā)送到交換器之后,是不會存儲消息的,而是通過中間層綁定關(guān)系將消息分發(fā)到不同的隊列上,其中交換器的分發(fā)策略分為四種:Direct、Topic、Headers、Fanout!
- Direct:直連類型,即在綁定時設(shè)定一個 routing_key, 消息的 routing_key 匹配時, 才會被交換器投送到綁定的隊列中去,原則是先匹配、后投送;
- Topic:按照規(guī)則轉(zhuǎn)發(fā)類型,支持通配符匹配,和 Direct 功能一樣,但是在匹配 routing_key的時候,更加靈活,支持通配符匹配,原則也是先匹配、后投送;
- Headers:頭部信息匹配轉(zhuǎn)發(fā)類型,根據(jù)消息頭部中的 header attribute 參數(shù)類型,將消息轉(zhuǎn)發(fā)到對應(yīng)的隊列,原則也是先匹配、后投送;
- Fanout:廣播類型,將消息轉(zhuǎn)發(fā)到所有與該交互機(jī)綁定的隊列上,不關(guān)心 routing_key;
Direct
Direct 是 RabbitMQ 默認(rèn)的交換機(jī)模式,也是最簡單的模式,消息中的路由鍵(routing key)如果和 Binding 中的 binding key 一致, 交換器就將消息發(fā)到對應(yīng)的隊列中。
如果傳入的 routing key 為 black,不會轉(zhuǎn)發(fā)到black.green。Direct 類型交換器是完全匹配、單播的模式。
圖片
Topic
Topic 類型交換器轉(zhuǎn)發(fā)消息和 Direct 一樣,不同的是:它支持通配符轉(zhuǎn)發(fā),相比 Direct 類型更加靈活!
兩種通配符:*只能匹配一個單詞,#可以匹配零個或多個。
如果傳入的 routing key 為 black#,不僅會轉(zhuǎn)發(fā)到black,也會轉(zhuǎn)發(fā)到black.green。
圖片
Headers
headers 也是根據(jù)規(guī)則匹配, 相比 direct 和 topic 固定地使用 routing_key , headers 則是通過一個自定義匹配規(guī)則的消息頭部類進(jìn)行匹配。
在隊列與交換器綁定時,會設(shè)定一組鍵值對規(guī)則,消息中也包括一組鍵值對( headers 屬性),當(dāng)這些鍵值對有一對, 或全部匹配時,消息被投送到對應(yīng)隊列。
此外 headers 交換器和 direct 交換器完全一致,但性能差很多,目前幾乎用不到了。
圖片
Fanout
Fanout 類型交換器與上面幾個不同,不管路由鍵或者是路由模式,會把消息發(fā)給綁定給它的全部隊列,如果配置了 routing_key 會被忽略,也被成為消息廣播模式。很像子網(wǎng)廣播,每臺子網(wǎng)內(nèi)的主機(jī)都獲得了一份復(fù)制的消息
fanout 類型轉(zhuǎn)發(fā)消息在四種類型中是最快的。
單節(jié)點部署
docker部署
參考文檔:https://hub.docker.com/_/rabbitmq
docker run -d --name RabbitMQ -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=123456 -p 15672:15672 rabbitmq:4.0-management
然后打開瀏覽器訪問服務(wù)器ip:15672,輸入我們創(chuàng)建的賬號密碼登錄即可。
rpm部署
安裝Erlang
RabbitMQ是采用 Erlang語言開發(fā)的,所以系統(tǒng)環(huán)境必須提供 Erlang環(huán)境,需要是安裝 Erlang
Erlang和RabbitMQ版本對照:https://www.rabbitmq.com/which-erlang.html
本次安裝4.0版本的RabbitMQ,需要的Erlang版本為26.2,
下載地址:https://packagecloud.io/rabbitmq/erlang
[root@tiaoban ~]# wget https://packagecloud.io/rabbitmq/erlang/packages/el/8/erlang-26.2.5.3-1.el8.x86_64.rpm/download.rpm -O ./erlang-26.2.5.3-1.el8.x86_64.rpm
[root@tiaoban ~]# ls
erlang-26.2.5.3-1.el8.x86_64.rpm
# 安裝erlang
[root@tiaoban ~]# rpm -ivh erlang-26.2.5.3-1.el8.x86_64.rpm
警告:erlang-26.2.5.3-1.el8.x86_64.rpm: 頭V4 RSA/SHA256 Signature, 密鑰 ID 6026dfca: NOKEY
Verifying... ################################# [100%]
準(zhǔn)備中... ################################# [100%]
正在升級/安裝...
1:erlang-26.2.5.3-1.el8 ################################# [100%]
# 驗證
[root@tiaoban ~]# erl -v
Erlang/OTP 26 [erts-14.2.5.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit:ns]
Eshell V14.2.5.3 (press Ctrl+G to abort, type help(). for help)
1>
安裝RabbitMQ
下載地址:https://www.rabbitmq.com/docs/install-rpm
在RabiitMQ安裝過程中需要依賴socat和logrotate插件,首先安裝該插件
[root@tiaoban ~]# dnf -y install socat logrotate
安裝RabbitMQ
[root@tiaoban ~]# wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v4.0.1/rabbitmq-server-4.0.1-1.el8.noarch.rpm
[root@tiaoban ~]# ls
erlang-26.2.5.3-1.el8.x86_64.rpm k8s rabbitmq-server-4.0.1-1.el8.noarch.rpm
[root@tiaoban ~]# rpm -ivh rabbitmq-server-4.0.1-1.el8.noarch.rpm
警告:rabbitmq-server-4.0.1-1.el8.noarch.rpm: 頭V4 RSA/SHA512 Signature, 密鑰 ID 6026dfca: NOKEY
Verifying... ################################# [100%]
準(zhǔn)備中... ################################# [100%]
正在升級/安裝...
1:rabbitmq-server-4.0.1-1.el8 ################################# [100%]
[/usr/lib/tmpfiles.d/rabbitmq-server.conf:1] Line references path below legacy directory /var/run/, updating /var/run/rabbitmq → /run/rabbitmq; please update the tmpfiles.d/ drop-in file accordingly.
啟動RabbitMQ服務(wù)
[root@tiaoban ~]# systemctl start rabbitmq-server
[root@tiaoban ~]# systemctl enable rabbitmq-server
Created symlink /etc/systemd/system/multi-user.target.wants/rabbitmq-server.service → /usr/lib/systemd/system/rabbitmq-server.service.
[root@tiaoban ~]# systemctl status rabbitmq-server
默認(rèn)情況下,rabbitmq沒有安裝web端的客戶端軟件,需要安裝才可以生效
[root@tiaoban ~]# rabbitmq-plugins enable rabbitmq_management
訪問服務(wù)器ip:15672,就可以看到管理界面
圖片
高可用集群部署
高可用集群方案
RabbitMQ的集群主要有兩種模式:普通集群模式和克隆隊列模式。
普通集群模式:集群中各個節(jié)點之間只會相互同步元數(shù)據(jù),消息數(shù)據(jù)不會被同步。不論是生產(chǎn)者還是消費者,假如連接到的節(jié)點上沒有存儲隊列數(shù)據(jù),那么內(nèi)部會將其轉(zhuǎn)發(fā)到存儲隊列數(shù)據(jù)的節(jié)點上進(jìn)行存儲。雖然說內(nèi)部可以實現(xiàn)轉(zhuǎn)發(fā),但是因為消息僅僅只是存儲在一個節(jié)點,所以這種普通集群模式并沒有達(dá)到高可用的目的。
克隆隊列模式:集群中各個節(jié)點之間不僅僅會同步元數(shù)據(jù),消息內(nèi)容也會在鏡像節(jié)點間同步,可用性更高。這種方案提升了可用性的同時,因為同步數(shù)據(jù)之間也會帶來網(wǎng)絡(luò)開銷從而在一定程度上會影響到性能。
程序通過訪問 KeepAlived 提供的 VIP(虛擬 ip)指定到其中一個Haproxy,然后 Haproxy 將訪問請求代理到其管理的多個 Rabbitmq Server 中的一個,從而實現(xiàn)了高可用、負(fù)載均衡的功能。
圖片
集群角色規(guī)劃
通過hk1和hk2兩臺服務(wù),部署HA-proxy和KeepAlived實現(xiàn)高可用和負(fù)載均衡服務(wù),通過VIP 192.168.10.90對外提供服務(wù),所有請求反向代理至mq1、mq2、mq3組成的RabbitMQ集群。
服務(wù)器IP | hostname | 節(jié)點服務(wù) |
192.168.10.90 | vip | |
192.168.10.91 | hk1 | HA proxy、KeepAlived |
192.168.10.92 | hk2 | HA proxy、KeepAlived |
192.168.10.93 | mq1 | RabbitMQ |
192.168.10.94 | mq2 | RabbitMQ |
192.168.10.95 | mq3 | RabbitMQ |
Rabbit MQ集群部署
MQ集群節(jié)點修改hosts解析和主機(jī)名(每臺mq節(jié)點執(zhí)行)
[root@mq1 ~]# cat /etc/hosts
192.168.10.93 mq1
192.168.10.94 mq2
192.168.10.95 mq3
部署Erlang和RabbitMQ服務(wù)(每臺mq節(jié)點執(zhí)行)
參考上文單節(jié)點部署流程,此處不再贅述。
啟動RabbitMQ服務(wù)(每臺mq節(jié)點執(zhí)行)
[root@mq1 ~]# systemctl enable rabbitmq-server --now
RabbitMQ集群節(jié)點需要共享一個Erlang cookie,默認(rèn)存儲在/var/lib/rabbitmq/.erlang.cookie。需要在所有節(jié)點上保持該文件一致:(mq1節(jié)點執(zhí)行)
[root@mq1 ~]# ls /var/lib/rabbitmq/.erlang.cookie
/var/lib/rabbitmq/.erlang.cookie
[root@mq1 ~]# scp /var/lib/rabbitmq/.erlang.cookie mq2:/var/lib/rabbitmq/ 100% 20 9.4KB/s 00:00
[root@mq1 ~]# scp /var/lib/rabbitmq/.erlang.cookie mq3:/var/lib/rabbitmq/
# 確保復(fù)制后的文件權(quán)限為400:
[root@mq2 ~]# chmod 400 /var/lib/rabbitmq/.erlang.cookie
在節(jié)點2和節(jié)點3上執(zhí)行以下命令,將它們加入到節(jié)點1的集群中(mq2和3節(jié)點執(zhí)行):
[root@mq2 ~]# systemctl restart rabbitmq-server
[root@mq2 ~]# rabbitmqctl stop_app
Stopping rabbit application on node rabbit@mq2 ...
[root@mq2 ~]# rabbitmqctl join_cluster rabbit@mq1
Clustering node rabbit@mq2 with rabbit@mq1
[root@mq2 ~]# rabbitmqctl start_app
Starting node rabbit@mq2 ...
在任何一個節(jié)點上查看集群狀態(tài)
[root@mq3 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@mq3 ...
Basics
Cluster name: rabbit@mq3
Total CPU cores available cluster-wide: 12
Disk Nodes
rabbit@mq1
rabbit@mq2
rabbit@mq3
Running Nodes
rabbit@mq1
rabbit@mq2
rabbit@mq3
Versions
rabbit@mq3: RabbitMQ 4.0.1 on Erlang 26.2.5.3
rabbit@mq1: RabbitMQ 4.0.1 on Erlang 26.2.5.3
rabbit@mq2: RabbitMQ 4.0.1 on Erlang 26.2.5.3
CPU Cores
Node: rabbit@mq3, available CPU cores: 4
Node: rabbit@mq1, available CPU cores: 4
Node: rabbit@mq2, available CPU cores: 4
Maintenance status
Node: rabbit@mq3, status: not under maintenance
Node: rabbit@mq1, status: not under maintenance
Node: rabbit@mq2, status: not under maintenance
安裝web端插件并設(shè)置賬戶密碼
# 所有mq節(jié)點執(zhí)行
[root@mq1 ~]# rabbitmq-plugins enable rabbitmq_management
# 其中一個節(jié)點執(zhí)行
[root@mq1 ~]# rabbitmqctl add_user admin 123.com
Adding user "admin" ...
Done. Dont forget to grant the user permissions to some virtual hosts! See 'rabbitmqctl help set_permissions' to learn more.
[root@mq1 ~]# rabbitmqctl set_user_tags admin administrator
Setting tags for user "admin" to [administrator] ...
[root@mq1 ~]# rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"
Setting permissions for user "admin" in vhost "/" ...
瀏覽器訪問mq1:15672,查看UI信息
圖片
此時,集群搭建完畢,但是默認(rèn)采用的模式“普通模式”,只會相互同步元數(shù)據(jù),消息數(shù)據(jù)不會被同步,如果某一個節(jié)點宕機(jī),則會導(dǎo)致該節(jié)點上的消息數(shù)據(jù)不可用。
創(chuàng)建克隆隊列策略
在之前的mq中,我們可以將所有隊列設(shè)置為鏡像隊列,即隊列會被復(fù)制到各個節(jié)點,各個節(jié)點元數(shù)據(jù)和消息內(nèi)容保持一致。從 RabbitMQ 3.8 開始,官方不推薦使用經(jīng)典的鏡像隊列,而是推薦使用 隊列克?。≦uorum Queues) 來代替鏡像隊列。Quorum Queues 提供了更穩(wěn)定的高可用性,尤其是在節(jié)點故障和網(wǎng)絡(luò)分區(qū)的情況下。
- name:策略名,可自定義
- pattern:隊列的匹配模式(正則表達(dá)式)
- "^" 可以使用正則表達(dá)式,比如"^queue_" 表示對隊列名稱以“queue_”開頭的所有 隊列進(jìn)行鏡像,而"^"表示匹配所有的隊列
- definition:鏡像定義,**x-queue-type: quorum**:指定隊列類型為 Quorum Queue。
- quorum.initial-group-size:指定 Quorum Queue 初始副本數(shù)量(通常與集群節(jié)點數(shù)量相匹配)
- max-length:設(shè)置隊列的最大長度,以限制存儲的消息數(shù)量。當(dāng)隊列中的消息數(shù)超過此限制時,RabbitMQ 會丟棄最早的消息。
- delivery-limit:設(shè)置消息在投遞失敗后的最大重試次數(shù)。達(dá)到這個次數(shù)后,消息會被轉(zhuǎn)移到死信隊列(DLX)。
- message-ttl:消息在隊列中的生存時間,單位為毫秒。
- priority:可選參數(shù),policy的優(yōu)先級。
圖片
HaProxy部署
以下操作在hk1和hk2機(jī)器執(zhí)行
安裝haproxy
[root@hk1 ~]# dnf -y install haproxy
修改配置文件
[root@hk1 ~]# cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak
[root@hk1 ~]# vim /etc/haproxy/haproxy.cfg
# 開啟管理員監(jiān)控頁面
listen admin_stats
bind *:8080 #監(jiān)聽的ip端口號
stats enable
stats refresh 30s #統(tǒng)計頁面自動刷新時間
stats uri /admin #訪問的uri ip:8080/admin
stats realm haproxy
stats auth admin:admin #認(rèn)證用戶名和密碼
stats hide-version #隱藏HAProxy的版本號
stats admin if TRUE #管理界面,如果認(rèn)證成功了,可通過webui管理節(jié)點
# 配置前端監(jiān)聽
frontend main
bind *:5672 # 監(jiān)聽地址
default_backend rabbitmq # 匹配后端服務(wù)
# 配置后端代理
backend rabbitmq # 后端服務(wù)名稱
server rabbitmq1 192.168.10.93:5672 check
server rabbitmq2 192.168.10.94:5672 check
server rabbitmq3 192.168.10.95:5672 check
啟動服務(wù)
[root@hk1 ~]# systemctl start haproxy.service
[root@hk1 ~]# systemctl enable haproxy.service
Created symlink /etc/systemd/system/multi-user.target.wants/haproxy.service → /usr/lib/systemd/system/haproxy.service.
[root@hk1 ~]# ss -tunlp | grep haproxy
udp UNCONN 0 0 0.0.0.0:59334 0.0.0.0:* users:(("haproxy",pid=2489,fd=6),("haproxy",pid=2487,fd=6))
tcp LISTEN 0 2048 0.0.0.0:5672 0.0.0.0:* users:(("haproxy",pid=2489,fd=7))
tcp LISTEN 0 2048 0.0.0.0:8080 0.0.0.0:* users:(("haproxy",pid=2489,fd=5))
訪問haproxy管理員頁面驗證配置是否生效。
圖片
確認(rèn)無誤后hk2服務(wù)器同樣的步驟配置。
KeepAlived部署
以下操作在hk1和hk2機(jī)器執(zhí)行,設(shè)備網(wǎng)卡名稱為ens160,VIP為192.168.10.90。
安裝軟件包
[root@hk1 ~]# dnf -y install keepalived
修改配置文件
[root@hk1 ~]# cp /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.bak
[root@hk1 ~]# vim /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
router_id LVS_DEVEL # vrrp協(xié)議路由標(biāo)識
}
vrrp_instance VI_1 {
state MASTER # 定義主從類型
interface ens160 # 網(wǎng)卡名稱
virtual_router_id 51 # vrrp唯一id號
priority 100 # 優(yōu)先級主大于備
advert_int 1 # 心跳檢測時間間隔
authentication { # 認(rèn)證類型和密碼
auth_type PASS
auth_pass 1111
}
virtual_ipaddress { # vip配置
192.168.10.90/24
}
}
啟動服務(wù)
[root@hk1 ~]# systemctl start keepalived.service
[root@hk1 ~]# systemctl enable keepalived.service
[root@hk1 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:0c:29:2a:b6:b3 brd ff:ff:ff:ff:ff:ff
altname enp3s0
inet 192.168.10.91/24 brd 192.168.10.255 scope global noprefixroute ens160
valid_lft forever preferred_lft forever
inet 192.168.10.90/24 scope global secondary ens160
valid_lft forever preferred_lft forever
此時可以看到vip 192.168.10.90綁定到了hk1服務(wù)器ens160網(wǎng)卡上。
同樣的操作配置hk2服務(wù)器,配置文件如下
! Configuration File for keepalived
global_defs {
router_id LVS_DEVEL
}
vrrp_instance VI_1 {
state BACKUP # 主備類型
interface ens160
virtual_router_id 51
priority 99 # 優(yōu)先級低于主
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.90/24
}
}
高可用測試
接下來停止hk1服務(wù)器,模擬異常故障,查看hk2服務(wù)器,vip已經(jīng)成功飄移過來
[root@hk2 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:0c:29:17:68:2a brd ff:ff:ff:ff:ff:ff
altname enp3s0
inet 192.168.10.92/24 brd 192.168.10.255 scope global noprefixroute ens160
valid_lft forever preferred_lft forever
inet 192.168.10.90/24 scope global secondary ens160
valid_lft forever preferred_lft forever
訪問vip的8080端口,可正常提供服務(wù)
圖片
web界面使用
進(jìn)入 web 管理界面之后,可以很清晰的看到分了 6 個菜單目錄,分別是:Overview、Connections、Channels、Exchanges、Queues、Admin。
- Overview:總覽概述,主要介紹 rabbitmq 一些基礎(chǔ)匯總等信息
圖片
- Connections:連接池管理,主要介紹客戶端連接等信息
圖片
- Channels:信道管理,主要介紹信道連接等信息
圖片
點擊具體某個具體的信道,可以看到對應(yīng)的消費隊列等信息。
圖片
- Exchanges:交換器管理,主要介紹交換器等信息
圖片
- Queues:隊列管理,主要介紹隊列等信息
圖片
- Admin:系統(tǒng)管理,主要介紹用戶、虛擬主機(jī)、權(quán)限等信息
圖片
下面,我們重點介紹一些如何通過 web 頁面來操作 rabbitMQ!
交換器管理
點擊進(jìn)入 Exchanges 菜單,最下面有一個Add a new exchange標(biāo)簽。點擊Add a new exchange,會展示如下信息!
圖片
- Name:交換器名稱
- Type:交換器類型
- Durability:是否持久化,Durable:持久化,Transient:不持久化
- Auto delete:是否自動刪除,當(dāng)最后一個綁定(隊列或者exchange)被unbind之后,該exchange 自動被刪除
- Internal:是否是內(nèi)部專用exchange,是的話,就意味著我們不能往該exchange里面發(fā)消息
- Arguments:參數(shù),是AMQP協(xié)議留給AMQP實現(xiàn)做擴(kuò)展使用的
我們先新建一個名稱為test.exchange,類型為direct的交換器,結(jié)果如下。
圖片
等會用于跟隊列關(guān)聯(lián)!
隊列管理
點擊進(jìn)入 Queues 菜單,最下面也有一個Add a new queue標(biāo)簽。
點擊標(biāo)簽,即可進(jìn)入添加隊列操作界面!
圖片
- Name:隊列名稱
- Durability:是否持久化,Durable:持久化,Transient:不持久化
- Auto delete:是否自動刪除,是的話,當(dāng)隊列內(nèi)容為空時,會自動刪除隊列
- Arguments:參數(shù),是AMQP協(xié)議留給AMQP實現(xiàn)做擴(kuò)展使用的
同樣的,新建一個名稱為test_queue的消息隊列,結(jié)果如下。
圖片
隊列新建好了之后,繼續(xù)來建立綁定關(guān)系!
綁定管理
建立綁定關(guān)系,既可以從隊列進(jìn)入也可以從交換器進(jìn)入。
如果是從交換器進(jìn)入,那么被關(guān)聯(lián)的對象就是隊列。
圖片
如果是從隊列進(jìn)入,那么被關(guān)聯(lián)的對象就是交換器。
圖片
我們選擇從隊列入手,被綁定的交換器是test.exchange。建立完成之后,在交換器那邊也可以看到對應(yīng)的綁定關(guān)系。
圖片
發(fā)送消息
最后,我們從交換器入手,選擇對應(yīng)的交換器,點擊Publish message標(biāo)簽,填寫對應(yīng)的路由鍵 key,發(fā)送一下數(shù)據(jù),查看數(shù)據(jù)是否發(fā)送到對應(yīng)的隊列中。
圖片
然后點擊進(jìn)入 Queues 菜單,查詢消息隊列基本情況。其中Ready表示待消費的消息數(shù),total表示總消息數(shù)。
圖片
接收消息
然后選擇hello-mq消息隊列,點擊Get messages標(biāo)簽,獲取隊列中的消息。結(jié)果如下,可以很清晰的看到,消息寫入到隊列!
圖片
rabbitmqctl命令
服務(wù)管理
rabbitmqctl stop [{pid_file}]
# 表示stop 在RabbitMQ服務(wù)器上運行的一個Erlang 節(jié)點,可以指定某一個 *pid_file*,表示會等待這個指定的程序結(jié)束
rabbitmqctl shutdown
# 表示終止RabbitMQ 服務(wù)器上的Erlang進(jìn)程,如果終止失敗,會返回非零數(shù)字
rabbitmqctl stop_app
# 表示終止RabbitMQ的應(yīng)用,但是Erlang節(jié)點還在運行。該命令典型的運行在一些需要RabbitMQ應(yīng)用被停止的管理行為之前,例如 reset
rabbitmqctl start_app
# 表示啟動RabbitMQ的應(yīng)用。該命令典型的運行在一些需要RabbitMQ應(yīng)用被停止的管理行為之后,例如 reset
rabbitmqctl wait {pid_file}
# 表示等待RabbitMQ應(yīng)用啟動。該命令會等待指定的pid file被創(chuàng)建,也就是啟動的進(jìn)程對應(yīng)的pid保存在這個文件中,然后RabbitMQ應(yīng)用在這個進(jìn)程中啟動。如果該進(jìn)程終止,沒有啟動RabbitMQ應(yīng)用,就會返回錯誤。
# 合適的pid file是有rabbitmq-server 腳本創(chuàng)建的,默認(rèn)保存在 Mnesia 目錄下,可以通過修改 RABBITMQ_PID_FILE 環(huán)境變量來修改
# 例如 rabbitmqctl wait /var/run/rabbitmq/pid
rabbitmqctl reset
# 表示設(shè)置RabbitMQ節(jié)點為原始狀態(tài)。會從該節(jié)點所屬的cluster中都刪除,從管理數(shù)據(jù)庫中刪除所有數(shù)據(jù),例如配置的用戶和vhost,還會刪除所有的持久消息。
# 要想reset和force_reset操作執(zhí)行成功,RabbitMQ應(yīng)用需要處于停止?fàn)顟B(tài),即執(zhí)行過 stop_app
rabbitmqctl force_reset
# 表示強(qiáng)制性地設(shè)置RabbitMQ節(jié)點為原始狀態(tài)。它和reset的區(qū)別在于,可以忽略目前管理數(shù)據(jù)庫的狀態(tài)和cluster的配置,無條件的reset。
# 該方法的使用,應(yīng)當(dāng)用在當(dāng)數(shù)據(jù)庫或者cluster配置損壞的情況下作為最后的方法。
rabbitmqctl rotate_logs {suffix}
# 表示將日志文件的內(nèi)容追加到新的日志文件中去,這個新的日志文件的文件名是原有的日志文件名加上命令中的 suffix,并且恢復(fù)日志到原來位置的新文件中。
# 注意:如果新文件原先不存在,那么會新建一個;如果suffix為空,那么不會發(fā)生日志轉(zhuǎn)移,只是重新打開了一次日志文件而已。
rabbitmqctl hipe_compile {directory}
# 表示在指定的目錄下執(zhí)行HiPE編譯和緩存結(jié)果文件 .beam-files
# 如果需要父目錄會被創(chuàng)建。并且在編譯之前,該目錄下的所有 .beam-files會被自動刪除。
# 使用預(yù)編譯的文件,你應(yīng)該設(shè)置 RABBITMQ_SERVER_CODE_PATH 環(huán)境變量為 hipe_compile 調(diào)用指定的目錄。
集群管理
rabbitmqctl join_cluster {clusternode} [--ram]
# 表示結(jié)合到指定的集群,如果有參數(shù) --ram 表示作為RAM節(jié)點結(jié)合到該集群中。
# 該命令指令本節(jié)結(jié)合到指定的集群中,在結(jié)合之前,該節(jié)點需要reset,所以在使用時,需要格外注意。為了成功運行本命令,必須要停止RabbitMQ應(yīng)用,例如 stop_app
# 集群節(jié)點有兩種類型: disc 和 RAM。disc類型,復(fù)制數(shù)據(jù)在RAM和disc上,在節(jié)點失效的情況下,提供了冗余保證,也能從一些全局事件中恢復(fù),例如所有節(jié)點失效。RAM類型,只復(fù)制數(shù)據(jù)在RAM上,主要表現(xiàn)在伸縮性上,特別是在管理資源(例如:增加刪除隊列,交換器,或者綁定)上表現(xiàn)突出。
# 一個集群必須至少含有一個disc節(jié)點,當(dāng)通常都多余一個。通過該命令時,默認(rèn)是設(shè)置為disc節(jié)點,如果需創(chuàng)建RAM節(jié)點,需要指定參數(shù) --ram
# 執(zhí)行此命令之后,在該節(jié)點上啟動的RabbitMQ應(yīng)用,在該節(jié)點掛掉之后,會嘗試連接節(jié)點所在集群中的其他節(jié)點。
# 為了離開集群,可以 reset 該節(jié)點,也可以使用命令 forget_cluster_node 遠(yuǎn)程刪除節(jié)點
rabbitmqctl cluster_status
# 表示顯示通過節(jié)點類型聚合在一起的集群中的所有節(jié)點,還有目前正在運行的節(jié)點
rabbitmqctl change_cluster_node_type {disc|ram}
# 表示改變集群節(jié)點的類型。該操作的正確執(zhí)行,必定會停止該節(jié)點。并且在調(diào)整一個node為ram類型時,該節(jié)點不能為該集群的唯一node
rabbitmqctl forget_cluster_node [--offline]
# 表示遠(yuǎn)程移除一個集群節(jié)點。要刪除的節(jié)點必須脫機(jī),如果沒有脫機(jī),需要使用 --offline 參數(shù)。當(dāng)使用 --offline 參數(shù)時,rabbitmqctl不會去連接節(jié)點,而是暫時變成節(jié)點,以便進(jìn)行變更。這在節(jié)點不能正常啟動時非常有用。在這種情況下,節(jié)點會成為集群元數(shù)據(jù)的規(guī)范來源(例如哪些隊列存在)。因此如果可以的話,應(yīng)該使用此命令在最新的節(jié)點上關(guān)閉。
# --offline 參數(shù)使節(jié)點從脫機(jī)節(jié)點上移除。使用場景主要是在所有節(jié)點脫機(jī),且最后一個節(jié)點無法聯(lián)機(jī)時,從而防止整個集群啟動。在其他情況不應(yīng)該使用,否則會導(dǎo)致不一致。
# 例如 rabbitmqctl -n hare@mcnulty forget_cluster_node rabbit@stringer
# 上述命令將從節(jié)點 hare@mcnulty 中移除節(jié)點 rabbit@stringer
rabbitmqctl rename_cluster_node {oldnode1} {newnode1} [oldnode2] [newnode2...]
# 表示在本地數(shù)據(jù)庫上修改集群節(jié)點名稱。該命令讓rabbitmqctl暫時成為一個節(jié)點來做出做變更。因此,本地的待修改的集群節(jié)點一定要完全停止,其他節(jié)點可以是online或者offline
rabbitmqctl update_cluster_nodes {clusternode}
# 表示指示已經(jīng)集群的節(jié)點在喚醒時聯(lián)系 {clusternode} 進(jìn)行集群。這與 join_cluster 命令不同,因為它不加入任何集群,它是檢查節(jié)點是否已經(jīng)在具有 {clusternode} 的集群中。
# 該命令的需求,是在當(dāng)一個節(jié)點offline時,修改了集群節(jié)點的情形下。例如:節(jié)點A和B聚群,節(jié)點A offline了,節(jié)點C和B集群,并且B節(jié)點離開了該集群,那么當(dāng)節(jié)點A起來的時候,A會嘗試連接B,但是由于B節(jié)點已經(jīng)不在該集群中,所以會失敗。
# 通過 update_cluster_nodes -n A C 將會解決上述問題。
rabbitmqctl force_boot
# 表示強(qiáng)制確保節(jié)點啟動,即使該節(jié)點并不是最后down的。
# 一般情況下,當(dāng)你同時shut down了RabbitMQ集群時,第一個重啟的節(jié)點應(yīng)該是最后一個down掉的,因為它可能已經(jīng)看到了其他節(jié)點發(fā)生的事情。但是有時候這并不可能:例如當(dāng)整個集群lose power,那么該集群的所有節(jié)點會認(rèn)為他們不是最后一個關(guān)閉的。
# 如果最后down的節(jié)點永久的lost,那么應(yīng)該優(yōu)先使用 rabbitmqctl forget_cluster_node --offline ,因為這將確保在丟失節(jié)點上的鏡像隊列得到優(yōu)先處理。
rabbitmqctl sync_queue [-p vhost] {queue}
# {queue} 表示待同步的隊列名稱
# 指引含有異步slaves的鏡像隊列去同步自身。當(dāng)隊列執(zhí)行同步化時,其將會被鎖定(指所有publishers發(fā)送出去的和consumers獲取到的隊列都會被鎖定)。為了成功執(zhí)行此命令,隊列必須要被鏡像。
# 注意,排除消息的異步隊列將最終被同步化,此命令主要運用于未被排除完全消息的隊列。
rabbitmqctl cancel_sync_queue [-p vhost] {queue}
# 指引一個正在同步的鏡像隊列停止此操作。
rabbitmqctl purge_queue [-p vhost] {queue}
# {queue} 表示待清空消息的隊列名稱
# 該命令表示清空隊列(即刪除隊列中的所有消息)
rabbitmqctl set_cluster_name {name}
# 設(shè)置集群的名稱。在連接中,集群的名稱被聲明在客戶端上,被同盟和插件用來記錄一個消息所在的位置。集群的名稱默認(rèn)來自于集群中第一個節(jié)點的主機(jī)名,但是可以被修改。
用戶管理
rabbitmq有一個默認(rèn)的賬號密碼guest,但該情況僅限于本機(jī)localhost進(jìn)行訪問,所以需要添加一個遠(yuǎn)程登錄的用戶
用戶管理命令
# 添加用戶
rabbitmqctl add_user 用戶名 密碼
# 設(shè)置用戶角色,分配操作權(quán)限
rabbitmqctl set_user_tags 用戶名 角色
# 為用戶添加資源權(quán)限(授予訪問虛擬機(jī)根節(jié)點的所有權(quán)限)
rabbitmqctl set_permissions -p / 用戶名 ".*" ".*" ".*"
# 修改密碼
rabbitmqctl change_ password 用戶名 新密碼
# 刪除用戶
rabbitmqctl delete_user 用戶名
# 查看用戶清單
rabbitmqctl list_users
角色種類:
- administrator:可以登錄控制臺、查看所有信息、并對rabbitmq進(jìn)行管理
- monToring:監(jiān)控者;登錄控制臺,查看所有信息
- policymaker:策略制定者;登錄控制臺指定策略
- managment:普通管理員;登錄控制
這里創(chuàng)建用戶admin,密碼123.com,設(shè)置adminstator角色,賦予所有權(quán)限
[root@tiaoban ~]# rabbitmqctl add_user admin 123.com
Adding user "admin" ...
Done. Dont forget to grant the user permissions to some virtual hosts! See 'rabbitmqctl help set_permissions' to learn more.
[root@tiaoban ~]# rabbitmqctl set_user_tags admin administrator
Setting tags for user "admin" to [administrator] ...
[root@tiaoban ~]# rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"
Setting permissions for user "admin" in vhost "/" ...
訪問控制
rabbitmqctl add_vhost {vhost}
# {vhost} 表示待創(chuàng)建的虛擬主機(jī)項的名稱
rabbitmqctl delete_vhost {vhost}
# 表示刪除一個vhost。刪除一個vhost將會刪除該vhost的所有exchange、queue、binding、用戶權(quán)限、參數(shù)和策略。
rabbitmqctl list_vhosts {vhostinfoitem ...}
# 表示列出所有的vhost。其中 {vhostinfoitem} 表示要展示的vhost的字段信息,展示的結(jié)果將按照 {vhostinfoitem} 指定的字段順序展示。這些字段包括:name(名稱) 和 tracing (是否為此vhost啟動跟蹤)。
# 如果沒有指定具體的字段項,那么將展示vhost的名稱。
rabbitmqctl set_permissions [-p vhost] {user} {conf} {write} {read}
# 表示設(shè)置用戶權(quán)限。 {vhost} 表示待授權(quán)用戶訪問的vhost名稱,默認(rèn)為 "/"; {user} 表示待授權(quán)反問特定vhost的用戶名稱; {conf}表示待授權(quán)用戶的配置權(quán)限,是一個匹配資源名稱的正則表達(dá)式; {write} 表示待授權(quán)用戶的寫權(quán)限,是一個匹配資源名稱的正則表達(dá)式; {read}表示待授權(quán)用戶的讀權(quán)限,是一個資源名稱的正則表達(dá)式。
# rabbitmqctl set_permissions -p myvhost tonyg "^tonyg-.*" ".*" ".*"
# 例如上面例子,表示授權(quán)給用戶 "tonyg" 在vhost為 `myvhost` 下有資源名稱以 "tonyg-" 開頭的 配置權(quán)限;所有資源的寫權(quán)限和讀權(quán)限。
rabbitmqctl clear_permissions [-p vhost] {username}
# 表示設(shè)置用戶拒絕訪問指定指定的vhost,vhost默認(rèn)值為 "/"
rabbitmqctl list_permissions [-p vhost]
# 表示列出具有權(quán)限訪問指定vhost的所有用戶、對vhost中的資源具有的操作權(quán)限。默認(rèn)vhost為 "/"。
# 注意,空字符串表示沒有任何權(quán)限。
rabbitmqctl list_user_permissions {username}
# 表示列出指定用戶的權(quán)限vhost,和在該vhost上的資源可操作權(quán)限。