- RabbitMQ采用Erlang編寫,需安裝語言庫才能運行RabbitMQ代理服務器。
- AMQP:高級消息隊列協議。
- Github代碼
理解消息通信
生產者和消費者
生產者創建消息并創建標簽,然后發布到代理服務器RabbitMQ。
消息包含兩部分內容
- 有效載荷:需傳輸的數據,可以是任何內容。
- 標簽:用于描述有效載荷,并指定誰將獲得消息的拷貝。
消費者連接到代理服務器,并訂閱到隊列。
消費者只會接收到有效載荷。在消息路由過程中,消息的標簽并沒有隨有效載荷一同傳遞。
建立RabbitMQ連接
- 創建
應用程序→RabbitMQ代理服務器
的TCP連接。- 在TCP連接上創建信道,AMQP命令也是通過信道發送的。每條信道都會被分配一個唯一的ID。
隊列
AMQP消息路由三要素
- 交換器
- 隊列
- 綁定
消費者通過以下兩種方式從特定的隊列中接收消息
- 連續獲得:通過AMQP的
basic.consume
命令訂閱。這樣做會將信道置為接收模式,直到取消對隊列的訂閱為止。訂閱了消息后,消費者在消費|拒絕最近接收的消息后,就能從隊列中自動接收下一條消息。- 獲得單條:通過AMQP的
basic.get
命令訂閱。basic.get
會訂閱消息,獲得單條消息,然后取消訂閱。
消費者接收到的每一條消息都必須進行確認,確認后RabbitMQ才會從隊列移除該消息
- 通過AMQP的
basic.ack
命令顯示地向RabbitMQ發送確認。- 訂閱到隊列時設置auto_ack參數為true,則消費者接收到消息,RabbitMQ會自動視其確認了消息。
拒絕接收消息
- 把消費者從RabbitMQ服務器斷開連接。
- 使用AMQP的
basic.reject
命令(RabbitMQ2.0.0+)
。其參數requeue可指定該消息是否發送給下一個消費者進行處理。在新版本中,該參數置為false會讓消息進入死信隊列,被拒絕而不重入隊列的消息,可用于檢測問題。
聲明隊列
- 使用AMQP的
queue.declare
命令來創建隊列。 - 若消費者在同一條信道上訂閱了另一個隊列,需先取消訂閱并將信道置為傳輸模式。
- 消費者創建隊列時需指定隊列名稱,若未指定Rabbit會分配一個隨機名稱并在
queue.declare
命令的響應中返回。
- 參數說明
- exclusive:是否私有,可用于限制一個隊列只有一個消費者。
- auto-delete:是否自動移除,可用于只有一個消費者服務。結合
exclusive
和auto-delete
,可實現當消費者斷開連接時,則移除隊列。- passive:檢測是否已存在隊列,存在則成功返回;否則不創建隊列且返回一個錯誤。
- 聲明一個已存在的隊列
- 參數完全匹配現存的隊列,Rabbit什么都不做,并成功返回。參數不匹配則會失敗。
生產者還是消費者來創建隊列
發送出去的消息如果路由到了不存在的隊列,Rabbit會忽略它們
- 避免消息進入黑洞而丟失,則生產者和消費者都應該嘗試去創建隊列。
- 可忽略消息進入黑洞而丟失,或者自定義方法來重新發布未處理消息,則可只讓自己的消費者來聲明隊列。
交換器和綁定
- 消息通過路由鍵綁定到交換器,服務器會根據路由將消息從交換器路由到隊列。
- 如果路由的消息不匹配任何綁定模式,則消息將進入黑洞。
使用交換器和綁定的優點
- 完成不同的使用場景
- 對于消息的生產者來說,它不需要關心服務器的另一端隊列和消費者的邏輯。
交換器四種類型
- direct:若路由鍵匹配,消息就被投遞到相應的隊列。交換器名稱為空白字符串。
- fanout:會將收到的消息廣播到所有綁定的隊列中。
- topic:可讓來自不同源頭的消息到達同一個隊列。
- headers:允許匹配AMQP消息的header而非路由鍵,headers和direct交換器完全一致,但性能會差很多,幾乎不用。
隊列綁定到交換器可使用通配符
-
.
可將路由鍵分割成若干個部分 -
*
匹配任意文本 -
#
匹配所有規則,沒有分割概念,將.
視為關鍵字的匹配部分
虛擬主機和隔離
虛擬主機vhost
- 默認vhost:/
- 默認用戶和密碼:guest
- Rabbit管理工具:RabbitMQ安裝目錄下的rabbitmqctl,該命令不帶參數則顯示幫助信息。
- 擁有自己的隊列,交換器和綁定。
- 擁有自己的權限機制,vhost之間是絕對隔離的。
- 可只運行一個Rabbit,然后按需啟動若干個vhost。
- 在RabbitMQ集群上創建vhost時,整個集群上都會創建該vhost。
rabbitmqctl參數
-
-n rabbit@[server_name|ip]
:指定遠程節點,默認為本地。需確保運行Rabbit節點的服務器和運行rabbitmqctl的工作站安裝了相同的Erlang cookie。
管理vhost
- 創建vhost:
rabbitmqctl add_vhost [vhost_name]
- 刪除vhost:
rabbitmqctl delete_vhost [vhost_name]
- 列出Rabbit服務器上的vhost:
rabbitmqctl list_vhosts
持久化策略
設置參數實現持久化
- 消息投遞模式設置為2(持久化)
- 發送到持久化的交換器
- 到達持久化的隊列
- 隊列和交換器的durable屬性:是否持久化。默認fasle。
- 消息的投遞模式設值為2:標記成持久化。
- 持久化方式實現:將持久化消息寫入持久化日志文件。
- 持久化消息在RabbitMQ內建集群環境下工作得并不好。
- 持久化消息→持久化交換器:Rabbit會在消息提交到日志文件后才發送響應。
- 持久化消息→非持久化隊列,則會將該消息從日志中移除。
AMQP事務
優點:
- 填補了生產者發布消息和RabbitMQ將它們提交到磁盤過程中差生錯誤的漏洞。
缺點:
- 會降低大約2~10倍的消息吞吐量。
- 會使生產者應用程序產生同步。
發送方確認模式
需要將信道置為confirm模式,只能通過重新創建信道來關閉該設置。
優點:
- 異步執行
- 更加輕量級,對Rabbit性能的影響幾乎可以忽略不計。
消息的生命周期
生產者
需要完成的任務
- 創建RabbitMQ連接
- 獲取信道
- 聲明交換器
- 創建消息
- 發布消息
- 關閉信道
- 關閉RabbitMQ連接
- 代碼展示
消費者
需要完成的任務
- 創建RabbitMQ連接
- 獲取信道
- 聲明交換器
- 聲明隊列
- 隊列和交換器綁定
- 消費信息
- 關閉信道
- 關閉RabbitMQ連接
- 代碼展示
*發送方確認模式進行確認投遞
- 若同時擁有眾多運行的信道,則需要為每條信道維護一個內部的ID計數器。
- 代碼展示
Rabbit的運行管理
- Erlang節點和Erlang應用程序。
- 節點:指代RabbitMQ服務器實例。
- JVM:一個實例一個應用程序。
- Erlang:一個節點可同時運行多個應用程序。
- 如果應用程序崩潰了,節點會自動嘗試重啟應用程序。
- RabbitMQ節點:通常指RabbitMQ應用程序和其所在的Erlang節點。
管理節點
- 啟動Erlang節點和Rabbit應用程序:
rabbitmq -server
,參數-detached
:后臺啟動。- 停止節點(應用程序和節點同時關閉):
rabbitmqctl stop
,參數-n rabbit@[hostname]
可指定遠程節點。- 只停止RabbitMQ程序:
rabbitmqctl stop_app
Rabbit配置文件
rabbitmq.config
- Erlang數據結構
- mnesia:Mnesia數據庫配置選項,Mnesia是RabbitMQ用來存儲交換器和隊列元數據的。Mnesia配置選項
- rabbit:Rabbit特定的配置選項
- 配置文件不能完成對RabbitMQ的訪問控制
管理權限
單個用戶可以跨越多個vhost進行授權。
用戶管理
- 創建用戶:
rabbitmqctl add_user username password
- 刪除用戶:
rabbitmqctl delete_user username
,訪問控制條目一并刪除。- 列出用戶:
rabbitmqctl list_users
- 修改用戶密碼:
rabbitmqctl change_password username new_password
Rabbit權限系統
- 創建權限:
rabbitmqctl set_permissions -p vhost_name username ".*" ".*" ".*"
,參數-p
指定vhost(默認為/),后面三個正則式參數分別對應:配置,寫,讀權限。- 列出指定vhost的權限信息:
rabbitmqctl list_permissions -p vhost_name
- 刪除權限:
rabbitmqctl clear_permissions -p vhost_name username
- 列出用戶在Rabbit上的所有權限信息:
rabbitmqctl list_user_permissions username
列出隊列,交換器和綁定相關信息
列出隊列以及隊列信息
- 列出隊列:
rabbitmqctl list_queues -p vhost_name
,參數-p
指定vhost(默認為/)- 列出隊列其他信息:
rabbitmqctl list_queues queue_info_item...
,queue_info_items選項列表
列出交換器信息
rabbitmqctl list_exchanges
:默認返回交換器名稱和類型。rabbitmqctl list_exchanges exchange_info_item...
:指定返回其他信息。exchange_info_items選項列表
列出綁定信息
rabbitmqctl list_bindings
,參數-p
可指定vhost(默認為/),默認返回的信息每行包含:交換器名稱,隊列名稱,路由鍵和參數。
RabbitMQ日志
LOG_BASE環境變量
- rabbitmq-server:
LOG_BASE=/var/log/rabbitmq
- rabbit-sasl.log:Erlang運行相關信息。
- rabbit.log:Rabbit運行相關信息,如網絡流量,用戶,交換器隊列等。
輪換日志
rabbitmqctl rotate_logs suffix
:切換新日志文件,原日志文件名+suffix
Erlang常見錯誤
Erlang cookie
- Erlang節點通過交換作為令牌的Erlang cookie以獲得認證。
- 通常存儲在~/.erlang.cookie
Erlang節點
- 長節點名name:
rabbit@hostname.network.tld
- 短節點名sname:
rabbit@hostname
,默認方式
Mnesia數據庫
- RabbitMQ啟動時會先啟動Mnesia
- MNESIA_BASE目錄寫權限,Mnesia基于主機名創建數據庫。
Erlang相關操作
- 啟動節點:
erl -sname node_name
- 列出已連接的節點:
node().
- 查看已運行的節點:
net_adm:name().
,使用net_adm
模塊調用name()
函數,此處顯示的RabbitMQ端口并不是AMQP連接的端口。- ping檢測其它節點:
net_adm:ping('rabbit@hostname').
,響應為pong則連接成功,為pang則連接失敗。需共享Erlang cookie。- 遠程執行rabbit函數:
rpc:call('rabbit@hostname',module_name,function_name,[show_items])
,如:rpc:call('rabbit@hostname',erlang,system_info,[process_count])
,rpc:call('rabbit@hostname',mnesia,info,[])
- 退出節點:
q().
RabbitMQ應用程序設計
從同步編程模型轉向異步編程模型
- 圖片上傳并行處理
- 告警通知郵件
- 實現分布式遠程過程調用
發后既忘處理模型
- 批處理:將單張圖片上傳并轉換成眾多其它尺寸和格式。
- 告警通知:不需要擔心發送目標和發送方式。
- 用戶積分獎勵
- 代碼展示
實現RPC遠程調用
私有隊列和發送確認
- 生產者通過
消息頭的reply_to
字段確定隊列名稱,并監聽隊列等待應答。- 消費者能夠檢查
reply_to字段
,然后創建包含應答內容的新的消息,并以隊列名稱作為路由鍵。- 此處可在用于消費的同一條信道上發布應答消息。
- 使用
reply_to
作為發布應答消息的目的地;發布時無須指定交換器。- 代碼展示