RabbitMQ入門

概念介紹

RabbitMQ總體上能夠看作是一個生產者和消費者模型,主要負責接收、存儲和轉發消息。java

生產者和消費者

Producer:生產者,就是投遞消息的一方。服務器

  生產者建立消息,而後發佈到RabbitMQ中,消息通常能夠包含兩個部分:消息體和標籤(Label)。消息體也能夠稱之爲payload,在實際的應用中,消息體通常是一個帶有業務邏輯結構的數據,好比一個Json字符串;消息的標籤用來表述這條消息,好比一個交換器的名稱和一個路由鍵。生產者把消息交由RabbitMQ,RabbitMQ以後會根據標籤把消息發送給感興趣的消費者。函數

Consumer:消費者,就是接收消息的一方。性能

  消費者鏈接到RabbitMQ服務器,並訂閱到隊列上。當消費者消費一條消息時,只消費消息的消息體(payload)。在消息路由的過程當中,消息的標籤會丟棄,存入到隊列中的消息只有消息體,消費者也只會消費消息體,也不知道消息的生產者是誰,固然消費者也不須要知道。操作系統

Broker:消息中間件的服務節點。線程

  對於RabbitMQ來講,一個RabbitMQ Broker能夠簡單的看做一個RabbitMQ服務節點或者RabbitMQ服務實例。大多數的狀況下能夠將一個RabbitMQ Broker看做一臺RabbitMQ服務器。debug

下圖展現了生產者將消息存入RabbitMQ Broker,以及消費者從Broker中消費數據的整個流程:3d

對上圖的流程進行簡單的說明:中間件

  首先是生產者將業務方的數據進行可能的包裝,以後封裝成消息,發送到Broker中。訂閱者訂閱並接收消息,通過可能的解包處理獲得原始的數據,以後再進行業務處理邏輯。這個業務處理邏輯並不必定須要和接收消息的邏輯使用同一個線程。消費者進程可使用一個線程去接收消息,存入到內存中,而業務處理邏輯使用另一個線程去讀取數據,這樣的話能夠將消息的處理進一步的解耦,提升整個應用的處理效率。對象

隊列

Queue:隊列,是RabbitMQ的內部對象,用於存儲消息。以下圖2-3所示

多個消費者能夠訂閱同一個隊列,這時隊列中的消息會被平分到(輪詢)給多個消費者處理,而不是每一個消費者都收到全部的消息並處理。以下圖2-4所示

交換器

Exchange:交換器。RabbitMQ中生產者將消息發送到Exchange(交換器,一般用大寫的X表示),由交換器將消息路由到一個或者多個隊列中。若是路由不到,或許會返回給生產者,或者直接丟棄。

路由鍵

RoutingKey:路由鍵。生產者將消息發送給交換器的時候,通常會指定一個RoutingKey,用來指定這個消息的路由規則,而這個RoutingKey須要與交換器類型和綁定鍵(BindingKey)聯合使用才能最終生效。

綁定

Binding:綁定。RabbitMQ中經過綁定將交換器與隊列關聯起來,在綁定的時候通常會指定一個綁定鍵,這樣RabbitMQ就知道如何正確的將消息路由到隊列了。以下圖2-6所示

生產者將消息發送給交換器時,須要一個RoutingKey,當BindingKey和RoutingKey相匹配時,消息就會被路由到對應的隊列中。在綁定多個隊列到同一個交換器的時候,這些綁定容許使用相同的BindingKey。BindingKey並非在全部的狀況下都有效,它依賴於交換器的類型,好比fanout類型就會無視BindingKey,而是將消息路由到全部綁定到該交換器的隊列中。

交換器類型

RabbitMQ經常使用的交換器類型有fanout、direct、topic、headers這四種。AMQP協議裏還提到另外兩種類型:System和自定義,這裏不予描述。

fanout

它會把全部發送到該交換器的消息路由到全部與該交換器綁定的隊列中。

direct

direct類型的交換器路由規則很簡單,它會把消息路由到那些BindingKey和RoutingKey徹底匹配的隊列中。

如下圖2-7爲例,交換器的類型爲direct,若是咱們發送一條消息,並在發送消息的時候設置路由鍵爲「warning」,則消息會路由到Queue1和Queue2:

若是在發送消息的時候將路由鍵設置爲「info」或者「debug」,消息只會路由到Queue2中,若是以其餘的路由鍵發送消息,則消息不會路由到這兩個隊列之中。

topic

topic類型的交換器在direct的匹配規則上進行了擴展,與direct類型的類似,也是將BindingKey和RoutingKey相匹配的隊列中,可是這裏的匹配規則有些不一樣,它約定:

  ❤ RoutingKey爲一個點號「.」分隔的字符串(被點號「.」分隔開的每一段獨立的字符串稱爲一個單詞),如「com.rabbitmq.client」、「java.util.concurrent」、「com.hidden.client」;

  ❤ BindingKey 和 RoutingKey同樣也是點號「.」分隔開的字符串;

  ❤ BindingKey中能夠存在兩種特殊的字符串「*」和「#」,用於模糊匹配,其中「*」用於匹配一個單詞。「#」用於匹配多個單詞(能夠是零個);

以圖2-8 中的配置爲例:

  • 路由鍵爲" com.rabbitmq.client" 的消息會同時路由到Queuel 和Queue2;
  • 路由鍵爲" com.hidden.client" 的消息只會路由到Queue2 中:
  • 路由鍵爲" com.hidden.demo" 的消息只會路由到Queue2 中:
  • 路由鍵爲"java.rabbitmq.demo" 的消息只會路由到Queuel 中:
  • 路由鍵爲" java.util..concurrent" 的消息將會被丟棄或者返回給生產者(須要設置mandatory參數),由於它每一匹配任何路由鍵。

headers

headers類型的交換器不依賴於路由鍵的匹配規則來路由消息,而是根據發送的消息內容中的headers屬性進行匹配。在綁定隊列和交換器時制定一組鍵值對,當發送消息到交換器時,RabbitMQ會獲取到該消息的headers(也是一組鍵值對的形式),對比其中的鍵值對是否徹底匹配隊列和交換器綁定時指定的鍵值對,若是徹底匹配則消息會路由到該隊列,不然不會路由到該隊列。headers類型的交換器性能會不好,並且也不實用,基本上不會看到它的存在。

RabbitMQ運轉流程

生產者發送消息的過程

(1)生產者鏈接到RabbitMQ Broker,創建一個鏈接(Connection),開啓一個信道(Channel);

(2)生產者聲明一個交換器,並設置相關屬性,好比交換器的類型、是否持久化等;

(3)生產者聲明一個隊列並設置相關的屬性,好比是否排他、是否持久化、是否自動刪除等;

(4)生產者經過路由鍵將交換器和隊列綁定起來;

(5)生產者發送消息至RabbitMQ Broker,其中包含路由鍵、交換器等信息;

(6)相應的交換器根據接收到的路由鍵查找相匹配的隊列;

(7)若是找到,則將從生產者發送過來的消息存入到相應的隊列中;

(8)若是沒有找到,則根據生產者的配置屬性選擇丟棄仍是退回給生產者;

(9)關閉信道;

(10)關閉鏈接。

消費者接收消息的過程

(1)消費者鏈接到RabbitMQ Broker,創建一個鏈接,開啓一個信道;

(2)消費者向RabbitMQ Broker請求消費相應隊列中的消息,可能會設置相應的回調函數,以及作一些準備工做;

(3)等待RabbitMQ Broker迴應並投遞相應隊列中的消息,消費者接收消息;

(4)消費者確認接收到的消息;

(5)RabbitMQ從隊列中刪除相應的已經被確認的消息;

(6)關閉信道;

(7)關閉鏈接。

在上述生產者和消費者的過程當中都涉及到了鏈接和信道。

Connection 和 Channel

Connection:一條TCP鏈接。

Channel:創建在Connection之上的虛擬鏈接,RabbitMQ處理的每條AMQP指令都是經過Channel完成的。

上圖2-9展現了Connection和Channel的關係。

可是有一個疑問,咱們徹底可使用Connection就能完成信道的工做,爲何還要引入信道呢?

  想一個這樣的場景,一個應用程序有不少個線程須要從RabbitMQ中消費消息,或者生產消息,那麼必然須要創建不少個Connection,也就是不少個TCP鏈接。然而對於操做系統而言,創建和銷燬TCP鏈接是很是昂貴的開銷,若是遇到使用高峯,性能瓶頸也隨之顯現。RabbitMQ採用NIO的作法,選擇TCP鏈接複用,不只能夠減小性能開銷,同時也便於管理。

   每一個線程把持一個信道,因此信道複用了Connection的TCP鏈接。同時RabbitMQ能夠確保每一個線程的私密性,就像擁有獨立的鏈接同樣。當每一個信道的流量不是很大時,複用單一的Connection能夠在產生性能瓶頸的狀況下有效的節省TCP鏈接資源。可是當信道自己的流量很大時,這時候多個信道複用一個Connection就會產生性能瓶頸,進而使得總體的流量被限制了,此時就須要開闢多個Connection,將這些信道均攤到這些Connection中。

 參考:《RabbitMQ實戰指南》 朱忠華 編著: 

相關文章
相關標籤/搜索