「消息隊列」RabbitMQ概述

轉載請註明出處: https://blog.csdn.net/jinixin/article/details/83964610

 

上篇文章談了消息隊列與AMQP是什麼, 本篇文章將接着爲大家介紹一款成熟的消息隊列產品--RabbitMQ, 圍繞其的安裝, 配置, 管理以及搭建分佈式集羣來簡單談談.

RabbitMQ是由Rabbit公司遵循AMQP協議開發的開源消息隊列產品, 經過多年的迭代, 已經演變成穩定的老牌消息隊列產品.

RabbitMQ是AMQP的一種實現, RabbitMQ Server是AMQP消息代理(Broker)的一種實現. RabbitMQ本質上是一個緩衝區, 其的大小隻受所在主機內存和磁盤大小的限制.

 

 

安裝RabbitMQ

 

1. mac的Homebrew方式安裝

brew install rabbitmq

 

2. Ubuntu的包管理器方式安裝

sudo apt-get install rabbitmq-server

 

3. 類unix系統的二進制方式安裝

1) 請先注意安裝Erlang, 再安裝RabbitMQ. 此外注意RabbitMQ需與Erlang的版本相匹配, 具體版本匹配, 請點擊這裏.
2) 選擇好Erlang版本後, 即可去Erlang官網下載. 解壓安裝包後, 可在README文件中找到安裝步驟, 安裝成功後請重啓計算機.
3) Erlang安裝完成後, 即可下載對應版本的RabbitMQ包. RabbitMQ不需要安裝, 解壓後即可運行sbin目錄下的命令.

 

4. 其他方式(包括docker)安裝

請點擊這裏查看

 

 

配置RabbitMQ

 

RabbitMQ有很多配置, 說實話我覺得現在連其的三分之一都沒搞清楚, 但這一點也不影響我們使用它.

我們可以依照這些配置所屬的不同文件將他們分爲普通配置, 高級配置和環境配置三類, 其對應的文件分別爲"rabbitmq.conf", "advanced.config"以及"rabbitmq-env.conf", 三個文件都被放置在RabbitMQ的配置目錄下. 如果在配置目錄下沒有想要的配置文件, 手動創建即可. 修改配置後, 請重啓RabbitMQ服務.

配置目錄的默認路徑爲"RabbitMQ安裝目錄/etc/rabbitmq/", 如果你是直接解壓RabbitMQ的, 則配置目錄的默認路徑爲"RabbitMQ解壓目錄/etc/rabbitmq/".

 

1. 環境配置(rabbitmq-env.conf)

配置字段 配置字段的作用說明
LOG_BASE RabbitMQ日誌的存儲目錄, 默認爲"RabbitMQ安裝目錄/var/log/rabbitmq/".
NODENAME

當前節點名稱, 這在集羣中很有用. 默認爲"rabbit@主機名", 其中主機名默認是"hostname -s"命令的結果.

RabbitMQ集羣各節點間是通過主機名相互交流的, 因此該節點如果要和其他節點一起構成集羣, 請務必確保主機名是域名(不能localhost)或IP, 即確保主機名可被同個子網中其他計算機ping通, 否則該節點將無法加入集羣.

USE_LONGNAME 節點名是否使用長名稱, 默認爲"false". 短節點名樣式爲"rabbit@xx", 長節點名樣式爲"[email protected]". 如果該節點會加入集羣, 我建議將該值置爲"true".
NODE_IP_ADDRESS 默認會綁定到所有IP, 通過此配置可綁定到特定IP.
NODE_PORT 端口, 默認是5672.
CONFIG_FILE 獲取普通配置文件(rabbitmq.conf)的路徑.
ADVANCED_CONFIG_FILE 獲取高級配置文件(advanced.config)的路徑.
CONF_ENV_FILE 獲取環境變量配置文件(rabbitmq-env.conf)路徑.

上面我列舉了自認爲比較常用的幾個配置, 所有的環境變量配置可點擊這裏獲得. 環境配置除了使用文件的方式來處理, 也可將配置寫入環境變量中, 但需要加上"RABBITMQ_"前綴, 比如"LOG_BASE"就要變爲"RABBITMQ_LOG_BASE"才能寫入環境變量.

 

2. 普通配置(rabbitmq.conf)

普通配置字段的介紹, 請點擊這裏. 普通配置的模板, 請點擊這裏.

 

3. 高級配置(advanced.config)

高級配置字段的介紹, 請點擊這裏. 高級配置的模板, 請點擊這裏.

 

4. 配置格式

3.7.0版本前僅支持使用Erlang語法進行配置, 3.7.0版本引入了sysctl格式. 相比於Erlang語法, sysctl格式更容易通過chef或puppet來產生, 也更便於人類閱讀.
sysctl格式, 也就是"key=val"的格式, 用"#"表示註釋. 額外請注意, 普通配置與環境配置支持Erlang語法與sysctl格式, 但高級配置只能用Erlang語法來搞, 官方給的解釋是高級配置非常複雜, sysctl格式無法應付.

 

 

啓動RabbitMQ

 

RabbitMQ服務可通過"RabbitMQ安裝目錄/sbin"下的"rabbitmq-server"命令來啓動, 具體如下:

 

1. 前臺啓動

通過"rabbitmq-server"可在前臺啓動服務, 啓動後當前命令行隨即block住, 這表示RabbitMQ的消息代理已經成功運行.

 

2. 後臺啓動

通過"rabbitmq-server -detached"可以後臺方式啓動服務, 雖然當前命令行不會block住, 但會導致pid不能被寫入pid文件, 從而收到"Warning: PID file not written; -detached was passed"的警告, 可忽略.

 

RabbitMQ服務啓動後, 可通過"rabbitmqctl"命令來結束服務. 我猜測, Rabbit服務應該包含兩部分, 其一是Erlang進程, 其二是RabbitMQ應用. "rabbitmq-server"將開啓一個Erlang進程, 並在該進程上啓動RabbitMQ應用.

 

 

RabbitMQ集羣

 

集羣一般由多個節點構成, 但不像其他的分佈式系統有主從節點之分, RabbitMQ中所有節點都是一致的. RabbitMQ集羣可通過多種方式構建, 比如下面會介紹到的自動節點發現, 或是通過rabbitmqctl手動搭建等. 集羣中的一個節點異常或恢復後, 集羣會自動下線/上線, 無需我們額外操作.

 

1. 集羣的搭建條件

1) 所有待組網計算機(節點)必須在同一子網中. 子網是通過中繼器或交換機相連接而組成的網絡, 通俗點可認爲是局域網.

2) 所有待組網計算機(節點)設置的主機名必須可以相互ping通, 具體原因我在"配置RabbitMQ-->環境配置"裏提過.

3) 所有待組網計算機(節點)必須具有相同的Erlang和RabbitMQ版本, 以及相同的cookie(下面會提).

 

2. 鏡像隊列

雖然RabbitMQ組成集羣后, 虛擬主機, 交換機, 用戶會自動在集羣的各個節點間拷貝, 但隊列以及其中的消息數據只會存在於一個節點上.

你也許會問, 當我想獲取某隊列的數據, 爲什麼在任意節點上都可以訪問到這個隊列呢? 原因是當客戶端連接某節點後, 如果該節點上沒有目標隊列, 該節點會自動將命令路由到目標節點的目標隊列上, 並取回數據.

比如一個集羣有A, B兩個節點, A節點上有Animal隊列, B節點上有Plant隊列. 我想訪問A節點Animal隊列上的數據, 但連接到了集羣的B節點, 由於Animal隊列默認在B節點上沒有拷貝, 所以B節點會替我連接A節點轉達命令並取回數據, 而我全程是無感的.

這樣也存在一個問題, 如果A節點發生了不可恢復的災難, 即使數據設置了持久化, 但A上的數據仍會全部丟失, 這將是非常糟糕的一件事, 怎麼預防呢? RabbitMQ提供了一種容錯方法, 即開通鏡像隊列. 其的具體介紹與配置, 請點擊這裏.

 

3. cookie

在"集羣的搭建條件"中, 我提到了cookie. 說實話, 剛看到cookie這個詞的時候, 我一臉吃驚. 莫非說的就是存在於客戶端的cookie? 用於請求時自動向服務器傳輸必要數據? 非也非也, 此cookie非彼cookie.

由於節點間要想交流, 必須具有相同的的祕鑰, 該祕鑰即Erlang cookie, 簡稱cookie. 該cookie不同於傳統的客戶端(瀏覽器)cookie, 其主要用於不同節點間互相識別身份, 只有具有相同cookie的節點, 纔會被認爲是同個集羣的, 兩者之間纔可以互相交換數據. Erlang cookie的一般存儲位置爲"用戶主目錄/.erlang.cookie".

那這個cookie是由誰在什麼時候產生的呢? 一般是當前節點的RabbitMQ服務啓動時, 如果不存在"用戶主目錄/.erlang.cookie"這一文件, 則Erlang虛擬機會自動創建, 並隨機賦值. Erlang cookie是一個長度不超過255的字符串.

 

4. 節點類型

節點按類型可以分爲磁盤節點與內存節點.

1) 磁盤節點

磁盤節點顧名思義, 是將交換機, 隊列以及消息都存儲到硬盤上.

 

2) 內存節點

內存節點則是在內存中保存交換機, 隊列這些數據, 因此其不會像磁盤節點那樣頻繁訪問硬盤, 所以會有更好的表現. 但注意隊列內的消息數據仍然會存儲在硬盤上, 故生產或消費數據並不會得到性能提升, 但對增加/刪除隊列這類操作還是有點作用的.

內存節點在啓動後會從集羣的其他節點上同步數據, 因此請確保集羣中至少存在一個磁盤節點. 內存節點非常脆弱, 一旦被關閉, 你將不能再重啓它, 並會丟失所有數據. 因此建議一個集羣爲了性能, 可以部署少量的內存節點, 但不可以全部爲內存節點.

 

3) 改變節點類型

我們可以在某個節點首次加入集羣時, 通過"--ram"標誌, 將其聲明爲內存節點, 如下:

rabbitmqctl stop_app
rabbitmqctl join_cluster --ram 節點名稱
rabbitmqctl start_app

也可以改變集羣已存在節點的類型:

rabbitmqctl change_cluster_node_type ram  # 將節點變爲內存節點
rabbitmqctl change_cluster_node_type disc  # 將節點變爲磁盤節點

 

6. 搭建集羣

簡單介紹三種, 前兩種爲通過配置自動搭建集羣(未驗證), 後一種爲手動搭建集羣(驗證可行).

 

1) 自動節點發現

第一種是在普通配置文件中指明集羣的所有節點:

cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config
cluster_formation.classic_config.nodes.1 = [email protected]  # 節點1
cluster_formation.classic_config.nodes.2 = [email protected]  # 節點2

第二種是在普通配置文件中配置DNS自動發現:

cluster_formation.peer_discovery_backend = rabbit_peer_discovery_dns
cluster_formation.dns.hostname = example.local  # 該域名下所有IP的對應主機都會成爲一個節點

若一個域名關聯了多個IP可以考慮使用這種方式, 即RabbitMQ服務通過DNS獲得所有IP地址, 再通過IP反向找到域名. 比如上面配置中的example.local對應有兩個IP地址: 192.168.100.1和192.168.100.2. 這些IP的反向DNS查找分別返回hostname1.example.local和hostname2.example.local. 因此該集羣將包含兩個節點: [email protected][email protected].

 

2) 通過rabbitmqctl手動搭建

假設同一子網中的node1節點與node2節點分別運行有RabbitMQ服務, 下面將把node2節點與node1節點組成集羣:

[1] 首先確保node1與node2節點都已滿足組成集羣的條件
[2] 通過"rabbit-server"分別啓動node1節點與node2節點上的RabbitMQ服務
[3] 在node2節點命令行上鍵入"rabbitmqctl stop_app", 用以關閉node2節點上的RabbitMQ應用, 但Erlang進程還活着
[4] 在node2節點命令行上鍵入"rabbitmqctl join_cluster node1的節點名稱", 用以將node2節點加入node1所在集羣
[5] 在node2節點命令行上鍵入"rabbitmqctl start_app", 用以打開node2節點上的RabbitMQ應用

 

7. 解散集羣

解散集羣一共有兩種方式, 本地操作(驗證可行)和遠程操作(未驗證). 下面會將"6"中創建的集羣解散, 即把node2節點從當前集羣中移出, 使其成爲獨立節點.

 

1) 本地操作

[1] 在node2節點命令行上鍵入"rabbitmqctl stop_app", 用於關閉node2節點上的RabbitMQ服務
[2] 在node2節點命令行上鍵入"rabbitmqctl reset", 用於重設node2節點, 此時集羣已解散
[3] 在node2節點命令行上鍵入"rabbitmqctl start_app", 用於打開node2節點上的RabbitMQ服務

 

2) 遠程操作

[1] 在node1節點命令行上鍵入"rabbitmqctl forget_cluster_node node2的節點名稱", 用以從集羣中移出node2節點.
[2] 執行完[1]中的操作後, node2節點還認爲其和node1節點是同一集羣的. 因此之後重啓node2節點, 會報"Error: inconsistent_cluster: Node XX thinks it's clustered with node"的錯誤. 此時只要在node2節點的命令行上鍵入"rabbitmqctl reset"進行重置, 再重啓即可.

 

 

雜談

 

下面簡單談下RabbitMQ中的"消息確認機制"與"消息持久化", 這兩個概念在上篇博文中就提過, 這裏再提一次來加深印象.

 

1. 消息確認機制

爲確保消息不丟失, RabbitMQ提供有消息確認機制, 默認是自動確認模式.

你也許會擔心某條消息的處理可能會非常耗時, 這樣一段時間後萬一觸發了RabbitMQ的超時, 進而導致RabbitMQ進行重發, 豈不是會導致消息被多次重複處理?

你不必擔心, 因爲只有消費者死亡時, 纔會進行重發. 如果只是耗時很長, RabbitMQ會耐心等待的, 這也是AMQP所規定的.

 

2. 消息持久化

上面的消息確認機制只是保證了消息不會丟失, 但如果某節點的RabbitMQ服務奔潰後, 該怎麼讓她回憶起曾經在一起的美好時光呢? 哦不是, 跑題了抱歉, 我還沒有過女朋友呢, 額不是, 收住收住, 嚴肅點.

該怎麼找回節點上的交換機, 隊列以及隊列裏的消息呢?

這個AMQP做了規定, 需要將交換機, 隊列與消息都標記爲持久化, 而後RabbitMQ會自動將數據存儲在磁盤上. 注意這三者(交換機, 隊列, 消息)都要標記爲持久化, 缺一不可.

而具體怎麼來設置, 這個將在本系列的第四篇文章中結合具體語言(Python)與庫(pika)給出.

 

文中如有不當之處, 還望包容和指出, 感謝.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章