標籤:操作 name 經驗 lis 方式 檔案中 上進 label 技術分享
RabbitMQ 整體上是一個生產者與消費者模型,主要負責接收、儲存和轉寄訊息。可以把消
息傳遞的過程想象成:當你將一個包裹送到郵局,郵局會暫存並最終將郵件通過郵遞員送到收
件人的手上, RabbitMQ 就好比由郵局、郵箱和郵遞員組成的一個系統。從電腦術語層面來說,
RabbitMQ 模型更像是一種交換器模型。
生產者和消費者
Producer: 生產者,就是投遞訊息的一方。
生產者建立訊息,然後發布到RabbitMQ 中。訊息一般可以包含2 個部分:訊息體和標籤
(Label)。訊息體也可以稱之為payload ,在實際應用中,訊息體一般是一個帶有商務邏輯結構
的資料,比如一個JSON 字串。當然可以進一步對這個訊息體進行序列化操作。訊息的標籤
用來表述這條訊息, 比如一個交換器的名稱和一個路由鍵。生產者把訊息交由RabbitMQ ,
RabbitMQ 之後會根據標籤把訊息發送給感興趣的消費者C Consumer ) 。
Consumer: 消費者, 就是接收訊息的一方。
消費者串連到RabbitMQ 伺服器,並訂閱到隊列上。當消費者消費一條訊息時, 只是消費
訊息的訊息體(payload ) 。在訊息路由的過程中, 訊息的標籤會丟棄, 存入到隊列中的訊息只
有訊息體,消費者也只會消費到訊息體, 也就不知道訊息的生產者是誰,當然消費者也不需要
知道。
Broker: 訊息中介軟體的服務節點。
對於RabbitMQ 來說, 一個RabbitMQ Broker 可以簡單地看作一個RabbitMQ 服務節點,
或者RabbitMQ 服務執行個體。大多數情況下也可以將一個RabbitMQ Broker 看作一台RabbitMQ
伺服器。
首先生產者將業務方資料進行可能的封裝, 之後封裝成訊息, 發送( AMQP 協議裡這個動
作對應的命令為Basic.Publish) 到Broker 中。消費者訂閱並接收訊息(AMQP 協議裡這個
動作對應的命令為Basic.Consume 或者Basic. Get) ,經過可能的解包處理得到原始的資料,
之後再進行業務處理邏輯。這個業務處理邏輯並不一定需要和接收訊息的邏輯使用同一個線程。
消費者進程可以使用一個線程去接收訊息,存入到記憶體中,比如使用Java 中的BlockingQueue 。
業務處理邏輯使用另一個線程從記憶體中讀取資料,這樣可以將應用進一步解稿,提高整個應用
的處理效率。
隊列
Queue: 隊列,是RabbitMQ 的內部對象,用於儲存訊息。參考圖2- 1.隊列可以用圖2-3
表示。
RabbitMQ 中訊息都只能儲存在隊列中,這一點和Katka 這種訊息中介軟體相反。Katka 將消
息儲存在topic (主題)這個邏輯層面,而相對應的隊列邏輯只是topic 實際隱藏檔中的位移
標識。RabbitMQ 的生產者生產訊息並最終投遞到隊列中,消費者可以從隊列中擷取訊息並消費。
多個消費者可以訂閱同一個隊列,這時隊列中的訊息會被平均分攤(Round-Robin ,即輪詢)
給多個消費者進行處理,而不是每個消費者都收到所有的訊息井處理,2-4 所示。
RabbitMQ 不支援隊列層面的廣播消費,如果需要廣播消費,需要在其上進行二次開發,處理邏輯會變得異常複雜,同時也不建議這麼做。
交換器、路由鍵、綁定
Exchange: 交換器。在圖2-4 中我們暫時可以理解成生產者將訊息投遞到隊列中,實際上
這個在RabbitMQ 中不會發生。真實情況是,生產者將訊息發送到Exchange (交換器,通常也
可以用大寫的"X" 來表示),由交換器將訊息路由到一個或者多個隊列中。如果路由不到,或
許會返回給生產者,或許直接丟棄。這裡可以將RabbitMQ 中的交換器看作一個簡單的實體,
更多的細節會在後面的章節中有所涉及。
RabbitMQ 中的交換器有四種類型,不同的類型有著不同的路由策略,這將在下一節的交換
器類型(Exchange Types) 中介紹。
RoutingKey: 路由鍵。生產者將訊息發給交換器的時候, 一般會指定一個RoutingKey ,用
來指定這個訊息的路由規則,而這個RoutingKey 需要與交換器類型和綁定鍵(BindingKey) 聯
合使用才能最終生效。
在交換器類型和綁定鍵(BindingKey) 固定的情況下,生產者可以在發送訊息給交換器時,
通過指定RoutingKey 來決定訊息流程向哪裡。
Binding: 綁定。RabbitMQ 中通過綁定將交換器與隊列關聯起來,在綁定的時候一般會指
定一個綁定鍵( BindingKey ) ,這樣RabbitMQ 就知道如何正確地將訊息路由到隊列了,2-6
所示。
生產者將訊息發送給交換器時, 需要一個RoutingKey , 當BindingKey 和RoutingKey 相匹
配時, 訊息會被路由到對應的隊列中。在綁定多個隊列到同一個交換器的時候, 這些綁定允許
使用相同的BindingKey。BindingKey 並不是在所有的情況下都生效,它依賴於交換器類型, 比
如fanout 類型的交換器就會無視BindingKey , 而是將訊息路由到所有綁定到該交換器的隊列中。
對於初學者來說, 交換器、路由鍵、綁定這幾個概念理解起來會有點晦澀, 可以對照著代
碼清單1-1 來加深理解。
沿用本章開頭的比喻, 交換器相當於投遞包裹的郵箱, RoutingKey 相當於填寫在包裹上的
地址, BindingKey 相當於包裹的目的地, 當填寫在包裹上的地址和實際想要投遞的地址相匹配
時, 那麼這個包裹就會被正確投遞到目的地, 最後這個目的地的"主人"一一隊列可以保留這
個包裹。如果填寫的地址出錯,郵遞員不能正確投遞到目的地, 包裹可能會回退給寄件人,也
有可能被丟棄。
有經驗的讀者可能會發現, 在某些情形下, RoutingKey 與BindingKey 可以看作同一個東西。
代碼清單2-1 所展示的是代碼清單1-1 中的部分代碼:
以上代碼聲明了一個direct 類型的交換器(交換器的類型在下一節會詳細講述),然後將交
換器和隊資料行繫結起來。注意這裡使用的字樣是"ROUTING_阻y",在本該使用BindingKey 的
channel .qu eueBind 方法中卻和channe l.basicPublish 方法同樣使用了RoutingKey ,
這樣做的潛台詞是:這裡的RoutingKey 和BindingKey 是同一個東西。在direct 交換器類型下,
RoutingKey 和BindingKey 需要完全符合才能使用,所以上面代碼中採用了此種寫法會顯得方便
許多。
但是在topic 交換器類型下, RoutingKey 和BindingKey 之間需要做模糊比對,兩者並不是
相同的。
BindingKey 其實也屬於路由鍵中的一種,官方解釋為: the routing key to use for the binding。
可以翻譯為:在綁定的時候使用的路由鍵。大多數時候,包括官方文檔和RabbitMQ Java API
中都把BindingKey 和RoutingKey 看作RoutingKey ,為了避免混淆,可以這麼理解:
- 在使用綁定的時候,其中需要的路由鍵是BindingKey 。涉及的用戶端方法如:
channel.exchangeBind 、channel .queueBind ,對應的AMQP 命令(詳情參見
2.2 節)為Exchange.Bind 、Queue.Bind 。
- 在發送訊息的時候,其中需要的路由鍵是RoutingKey 。涉及的用戶端方法如
channel .basicPublish ,對應的AMQP 命令為Basic.Publish 。
由於某些曆史的原因,包括現存能搜集到的資料顯示:大多數情況下習慣性地將BindingKey
寫成RoutingKey ,尤其是在使用direct 類型的交換器的時候。本文後面的篇幅中也會將兩者合
稱為路由鍵,讀者需要注意區分其中的不同,可以根據上面的辨別方法進行有效區分。
交換器類型
RabbitMQ 常用的交換器類型有fanout 、direct、topic 、headers 這四種。AMQP 協議裡還提
到另外兩種類型: System 和自訂,這裡不予描述。對於這四種類型下面一一闡述。
fanout
它會把所有發送到該交換器的訊息路由到所有與該交換器綁定的隊列中。
direct
direct 類型的交換器路由規則也很簡單,它會把訊息路由到那些BindingKey 和RoutingKey
完全符合的隊列中。
以圖2-7 為例,交換器的類型為direct,如果我們發送一條訊息,並在發送訊息的時候設定
路由鍵為" warning" ,則訊息會路由到Queuel 和Queue2 ,對應的範例程式碼如下:
channel.basicPublish(EXCHANGE_NAME, "warning" ,
MessageProperties .PERSISTENT TEXT PLAIN,
message.getBytes()) ;
如果在發送訊息的時候設定路由鍵為" info" 或者"debug" ,訊息只會路由到Queue2 。如
果以其他的路由鍵發送訊息,則訊息不會路由到這兩個隊列中。
topic
前面講到direct 類型的交換器路由規則是完全符合BindingKey 和RoutingKey ,但是這種嚴
格的匹配方式在很多情況下不能滿足實際業務的需求。topic 類型的交換器在匹配規則上進行了
擴充,它與direct 類型的交換器相似,也是將訊息路由到BindingKey 和RoutingKey 相匹配的隊
列中,但這裡的匹配規則有些不同,它約定:
- RoutingKey 為一個點號". "分隔的字串(被點號" ."分隔開的每一段獨立的字元
串稱為一個單詞),如"com.rabbitmq.client","java,util.concurrent","com.hidden,client";
headers
headers 類型的交換器不依賴於路由鍵的匹配規則來路由訊息,而是根據發送的訊息內容中
的headers 屬性進行匹配。在綁定隊列和交換器時制定一組索引值對, 當發送訊息到交換器時,
RabbitMQ 會擷取到該訊息的headers (也是一個索引值對的形式) ,對比其中的索引值對是否完全
匹配隊列和交換器綁定時指定的索引值對,如果完全符合則訊息會路由到該隊列,否則不會路由
到該隊列。headers 類型的交換器效能會很差,而且也不實用,基本上不會看到它的存在。
rabbitMQ 基本概念