1 RabbitMQ安裝部署
這里是ErLang環境的下載地址
http://www.erlang.org/downloads
這是RabbitMQ環境、客戶端、實例和說明文檔的地址
http://www.rabbitmq.com/download.html
默認安裝的Rabbit MQ 監聽端口是:5672
進入目錄
CD D:\Program Files\RabbitMQ Server\rabbitmq_server-3.6.10\sbin
安裝完以后erlang需要手動設置ERLANG_HOME 的系統變量。
set ERLANG_HOME=D:\Program Files\erl9.0
激活Rabbit MQ's Management Plugin
rabbitmq-plugins.bat enable rabbitmq_management
創建管理用戶,所有的賬號設置,都是區分大小寫的,千萬注意
rabbitmqctl.bat add_user BOEREms BOEREms1703
設置管理員,設置管理員前,BOEREms這個賬號要先創建,否則設置就會失敗。
rabbitmqctl.bat set_user_tags BOEREms administrator
設置權限
rabbitmqctl.bat set_permissions -p / BOEREms ".*" ".*" ".*"
查詢用戶
rabbitmqctl.bat list_users
查詢vhosts
rabbitmqctl.bat list_vhosts
啟動RabbitMQ服務:
net stop RabbitMQ && net start RabbitMQ, 可以通過工具啟動和關閉RabbitMQ服務
訪問Rabbit Mq的管理控制臺,使用剛才創建的賬號登陸系統即可。
Rabbit MQ 管理后臺,可以更好的可視化方式查看RabbitMQ服務器實例的狀態。
http://localhost:15672
Virtual Host:UploadQueue
D:\Program Files\RabbitMQ Server\rabbitmq_server-3.6.10\sbin>rabbitmq-plugins.ba
t enable rabbitmq_management
Plugin configuration unchanged.
Applying plugin configuration to rabbit@WIN-7BVDCM2AUG0... failed.
* Could not contact node rabbit@WIN-7BVDCM2AUG0.
Changes will take effect at broker restart.
* Options: --online - fail if broker cannot be contacted.
--offline - do not try to contact broker.
D:\Program Files\RabbitMQ Server\rabbitmq_server-3.6.10\sbin>rabbitmqctl status
Status of node 'rabbit@WIN-7BVDCM2AUG0'
Error: unable to connect to node 'rabbit@WIN-7BVDCM2AUG0': nodedown
DIAGNOSTICS
===========
attempted to contact: ['rabbit@WIN-7BVDCM2AUG0']
rabbit@WIN-7BVDCM2AUG0:
* connected to epmd (port 4369) on WIN-7BVDCM2AUG0
* epmd reports node 'rabbit' running on port 25672
* TCP connection succeeded but Erlang distribution failed
* Authentication failed (rejected by the remote node), please check the Erlang
cookie
current node details:
- node name: 'rabbitmq-cli-17@WIN-7BVDCM2AUG0'
- home dir: C:\Users\Administrator
- cookie hash: /352utUNRCCPsDbXHre2Dw==
具體操作:將 C:\WINDOWS\.erlang.cookie 同步到RabbitMq 啟動用戶 例如win10:C:\Users\%USERNAME%\.erlang.cookie
2 RabbitMQ原理簡介
首先這個過程走分三個部分,1、客戶端(生產消息隊列),2、RabbitMQ服務端(負責路由規則的綁定與消息的分發),3、客戶端(消費消息隊列中的消息);由圖可以看出,一個消息可以走一次網絡卻被分發到不同的消息隊列中,然后被多個的客戶端消費,那么這個過程就是RabbitMQ的核心機制,RabbitMQ的路由類型與消費模式。
RabbitMQ中間件分為服務端(RabbitMQ Server)和客戶端(RabbitMQ Client),服務端可以理解為是一個消息的代理消費者,客戶端又分為消息生產者(Producer)和消息消費者(Consumer)。
- 消息生產者(Producer):主要生產消息并將消息基于TCP協議,通過建立Connection和Channel,將消息傳輸給RabbitMQ Server,對于Producer而言基本就完成了工作。
- 服務端(RabbitMQ Server):主要負責處理消息路由、分發、入隊列、緩存和出列。主要由三部分組成:Exchange、RoutingKey、Queue。
- Exchange:用于接收消息生產者發送的消息,有三種類型的exchange:direct, fanout,topic,不同類型實現了不同的路由算法;
A. direct exchange:將與routing key 比配的消息,直接推入相對應的隊列,創建隊列時,默認就創建同名的routing key。
B. fanout exchange:是一種廣播模式,忽略routingkey的規則。
C. topic exchange:應用主題,根據key進行模式匹配路由,例如:若為abc則推入到所有abc相對應的queue;若為abc.#則推入到abc.xx.one ,abc.yy.two對應的queue。
- RoutingKey:是RabbitMQ實現路由分發到各個隊列的規則,并結合Binging提供于Exchange使用將消息推送入隊列;
- Queue:是消息隊列,可以根據需要定義多個隊列,設置隊列的屬性,比如:消息移除、消息緩存、回調機制等設置,實現與Consumer通信;
- 消息消費者(Consumer):主要負責消費Queue的消息,同樣基于TCP協議,通過建立Connection和Channel與Queue傳輸消息,一個消息可以給多個Consumer消費;
- 關鍵名詞說明:Connection、Channel、Binging等;
- Connection:是建立客戶端與服務端的連接。
- Channel:是基于Connection之上建立通信通道,因為每次Connection建立TCP協議通信開銷及性能消耗較大,所以一次建立Connection后,使用多個Channel通道通信減少開銷和提高性能。
- Binging:是一個捆綁定義,將exchange和queue捆綁,定義routingkey相關策略。
3 RabbitMQ中的一些名詞闡述與消息從投遞到消費的整個過程
從上圖的標題中可以看到一些陌生的英文單詞,讓我們感覺一無所知,更無從操作,那么我給大家弄啦一個圖片大家可以看下,或許對您理解這些新鮮的單詞有所幫助。
看過這些名詞,之后,或許你還毫無頭緒,那么我把消息從生產到消費的整個流程給大家說一下,或許會更深入一點,其中Exchange,與Queue都是可以設置相關屬性,隊列的持久化,交換器類型制定。
4 RabbitMQ中Exchange的類型
Direct Exchange是RabbitMQ默認的交換機模式,也是最簡單的模式,根據key全文匹配去尋找隊列。
類型有4種,direct,fanout,topic,headers。其中headers不常用,本篇不做介紹,其他三種類型,會做詳細介紹。
那么這些類型是什么意思呢?就是Exchange與隊列進行綁定后,消息根據exchang的類型,按照不同的綁定規則分發消息到消息隊列中,可以是一個消息被分發給多個消息隊列,也可以是一個消息分發到一個消息隊列。具體請看下文。
介紹之初還要說下RoutingKey,這是個什么玩意呢?他是exchange與消息隊列綁定中的一個標識。有些路由類型會按照標識對應消息隊列,有些路由類型忽略routingkey。具體看下文。
- Exchange類型direct
他是根據交換器名稱與routingkey來找隊列的。
第一個 X - Q1 就有一個 binding key,名字為 orange; X - Q2 就有 2 個 binding key,名字為 black 和 green。當消息中的 路由鍵 和 這個 binding key 對應上的時候,那么就知道了該消息去到哪一個隊列中。
Ps:為什么 X 到 Q2 要有 black,green,2個 binding key呢,一個不就行了嗎? - 這個主要是因為可能又有 Q3,而Q3只接受 black 的信息,而Q2不僅接受black 的信息,還接受 green 的信息。
Note:消息從client發出,傳送給交換器ChangeA,RoutingKey為routingkey.ZLH,那么不管你發送給Queue1,還是Queue2一個消息都會保存在Queue1,Queue2,Queue3,三個隊列中。這就是交換器的direct類型的路由規則。只要找到路由器與routingkey綁定的隊列,那么他有多少隊列,他就分發給多少隊列。
- Exchange類型fanout
這個類型忽略Routingkey,他為廣播模式。
Note:消息從客戶端發出,只要queue與exchange有綁定,那么他不管你的Routingkey是什么他都會將消息分發給所有與該exchang綁定的隊列中。
- Exchange類型topic
這個類型的路由規則如果你掌握啦,那是相當的好用,與靈活。他是根據RoutingKey的設置,來做匹配的,其中這里還有兩個通配符為:
*,代表任意的一個詞。例如topic.zlh.*,他能夠匹配到,topic.zlh.one ,topic.zlh.two ,topic.zlh.abc, ....
#,代表任意多個詞。例如topic.#,他能夠匹配到,topic.zlh.one ,topic.zlh.two ,topic.zlh.abc, ....
Note:這個圖看上去很亂,但是他是根據匹配符做匹配的,這里我建議你自己做下消息隊列的具體操作。
-
Headers Exchange
headers 也是根據規則匹配, 相較于 direct 和 topic 固定地使用 routing_key , headers 則是一個自定義匹配規則的類型.
在隊列與交換器綁定時, 會設定一組鍵值對規則, 消息中也包括一組鍵值對( headers 屬性), 當這些鍵值對有一對, 或全部匹配時, 消息被投送到對應隊列.
5 消息隊列的消費與消息確認Ack
- 消息隊列的消費
Note:如果一個消息隊列中有大量消息等待操作時,我們可以用多個客戶端來處理消息,這里的分發機制是采用負載均衡算法中的輪詢。第一個消息給A,下一個消息給B,下下一個消息給A,下下下一個消息給B......以此類推。
- 為啦保證消息的安全性,保證此消息被正確處理后才能在服務端的消息隊列中刪除。那么rabbitmq提供啦ack應答機制,來實現這一功能。ack應答有兩種方式:1、自動應答,2、手動應答。
6 AMQP
AMQP(Advanced Message Queuing Protocol)協議是一個高級抽象層消息通信協議,RabbitMQ是AMQP協議的實現。它主要包括以下組件:
- Server(broker): 接受客戶端連接,實現AMQP消息隊列和路由功能的進程。
- Virtual Host:其實是一個虛擬概念,類似于權限控制組,一個Virtual Host里面可以有若干個Exchange和Queue,但是權限控制的最小粒度是Virtual Host。
- Exchange:接受生產者發送的消息,并根據Binding規則將消息路由給服務器中的隊列。ExchangeType決定了Exchange路由消息的行為,例如,在RabbitMQ中,ExchangeType有direct、Fanout和Topic三種,不同類型的Exchange路由的行為是不一樣的。
- Message Queue:消息隊列,用于存儲還未被消費者消費的消息。
- Message: 由Header和Body組成,Header是由生產者添加的各種屬性的集合,包括Message是否被持久化、由哪個Message Queue接受、優先級是多少等。而Body是真正需要傳輸的APP數據。
- Binding:Binding聯系了Exchange與Message Queue。Exchange在與多個Message Queue發生Binding后會生成一張路由表,路由表中存儲著Message Queue所需消息的限制條件即Binding Key。當Exchange收到Message時會解析其Header得到Routing Key,Exchange根據Routing Key與Exchange Type將Message路由到Message Queue。Binding Key由Consumer在Binding Exchange與Message Queue時指定,而Routing Key由Producer發送Message時指定,兩者的匹配方式由Exchange Type決定。
- Connection:連接,對于RabbitMQ而言,其實就是一個位于客戶端和Broker之間的TCP連接。
- Channel:信道,僅僅創建了客戶端到Broker之間的連接后,客戶端還是不能發送消息的。需要為每一個Connection創建Channel,AMQP協議規定只有通過Channel才能執行AMQP的命令。一個Connection可以包含多個Channel。之所以需要Channel,是因為TCP連接的建立和釋放都是十分昂貴的,如果一個客戶端每一個線程都需要與Broker交互,如果每一個線程都建立一個TCP連接,暫且不考慮TCP連接是否浪費,就算操作系統也無法承受每秒建立如此多的TCP連接。RabbitMQ建議客戶端線程之間不要共用Channel,至少要保證共用Channel的線程發送消息必須是串行的,但是建議盡量共用Connection。
- Command:AMQP的命令,客戶端通過Command完成與AMQP服務器的交互來實現自身的邏輯。例如在RabbitMQ中,客戶端可以通過publish命令發送消息,txSelect開啟一個事務,txCommit提交一個事務。
在了解了AMQP模型以后,需要簡單介紹一下AMQP的協議棧,AMQP協議本身包括三層:
- Module Layer,位于協議最高層,主要定義了一些供客戶端調用的命令,客戶端可以利用這些命令實現自己的業務邏輯,例如,客戶端可以通過queue.declare聲明一個隊列,利用consume命令獲取一個隊列中的消息。
- Session Layer,主要負責將客戶端的命令發送給服務器,在將服務器端的應答返回給客戶端,主要為客戶端與服務器之間通信提供可靠性、同步機制和錯誤處理。
- Transport Layer,主要傳輸二進制數據流,提供幀的處理、信道復用、錯誤檢測和數據表示。
7 RabbitMQ使用場景
場景1:單發送單接收
使用場景:簡單的發送與接收,沒有特別的處理。
場景2:單發送多接收
使用場景:一個發送端,多個接收端,如分布式的任務派發。為了保證消息發送的可靠性,不丟失消息,使消息持久化了。同時為了防止接收端在處理消息時down掉,只有在消息處理完成后才發送ack消息。
發送端和場景1不同點:
- 使用“task_queue”聲明了另一個Queue,因為RabbitMQ不容許聲明2個相同名稱、配置不同的Queue
- 使"task_queue"的Queue的durable的屬性為true,即使消息隊列durable
- 使用MessageProperties.PERSISTENT_TEXT_PLAIN使消息durable
接收端和場景1不同點:
- 使用“task_queue”聲明消息隊列,并使消息隊列durable
- 在使用channel.basicConsume接收消息時使autoAck為false,即不自動會發ack,由channel.basicAck()在消息處理完成后發送消息。
- 使用了channel.basicQos(1)保證在接收端一個消息沒有處理完時不會接收另一個消息,即接收端發送了ack后才會接收下一個消息。在這種情況下發送端會嘗試把消息發送給下一個not busy的接收端。
場景3:Publish/Subscribe
使用場景:發布、訂閱模式,發送端發送廣播消息,多個接收端接收。
發送端:
發送消息到一個名為“logs”的exchange上,使用“fanout”方式發送,即廣播消息,不需要使用queue,發送端不需要關心誰接收。
接收端:
- 聲明名為“logs”的exchange的,方式為"fanout",和發送端一樣。
- channel.queueDeclare().getQueue();該語句得到一個隨機名稱的Queue,該queue的類型為non-durable、exclusive、auto-delete的,將該queue綁定到上面的exchange上接收消息。
- 注意binding queue的時候,channel.queueBind()的第三個參數Routing key為空,即所有的消息都接收。如果這個值不為空,在exchange type為“fanout”方式下該值被忽略!
場景4:Routing (按路線發送接收)
使用場景:發送端按routing key發送消息,不同的接收端按不同的routing key接收消息。
發送端和場景3的區別:
- exchange的type為direct
- 發送消息的時候加入了routing key
接收端和場景3的區別:
在綁定queue和exchange的時候使用了routing key,即從該exchange上只接收routing key指定的消息。
場景5:Topics (按topic發送接收)
使用場景:發送端不只按固定的routing key發送消息,而是按字符串“匹配”發送,接收端同樣如此。
發送端和場景4的區別:
- exchange的type為topic
- 發送消息的routing key不是固定的單詞,而是匹配字符串,如".lu.#",匹配一個單詞,#匹配0個或多個單詞。
接收端和場景4的區別:
- exchange的type為topic
- 接收消息的routing key不是固定的單詞,而是匹配字符串。