1. 程式人生 > >「訊息佇列」RabbitMQ概述

「訊息佇列」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

當前節點名稱, 這在叢集中很有用. 預設為"[email protected]主機名", 其中主機名預設是"hostname -s"命令的結果.

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

USE_LONGNAME 節點名是否使用長名稱, 預設為"false". 短節點名樣式為"[email protected]", 長節點名樣式為"[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)給出.

 

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