手把手帶你瞭解消息中間件(3)——RabbitMQ

RabbitMQ官網: http://www.rabbitmq.com/
Erlang官網:https://www.erlang.org
友情提示:RabbitMQ基於Erlang語言開發的,要想使用RabbitMQ的話,須要先安裝Erlang環境

1.RabbitMQ是什麼

  RabbitMQ是消息代理:它接受並轉發消息。能夠將它視爲郵局:當你投遞郵件到一個郵箱,郵遞員終究會將郵件遞交給你的收件人。以此類推,RabbitMQ能夠是一個郵箱,一個郵局和一個郵遞員。
  RabbitMQ與郵局之間的主要區別在於,它不處理紙張,而是接收,存儲和轉發數據消息的二進制數據。java

2.首先先了解幾個概念

  2.1 生產者(producer)與消費者(consumer)
  生產者與消費者是RabbitMQ通訊過程當中的兩個重要的角色,至關於郵件的發送方與接收方,而RabbitMQ充當的角色就是傳遞消息的第三方,也就是說它是不能產生數據的。在實際應用中,生產者和消費者也是能夠角色互相轉換的,因此當咱們應用程序鏈接到 RabbitMQ 服務器時,必需要明確我是生產者仍是消費者。緩存

  2.2 鏈接(Connections)
  生產者生產消息以後,發佈到RabbitMQ以前,須要先鏈接RabbitMQ服務器吧,RabbitMQ支持的全部協議都是基於TCP的,而且爲了提升效率而採用長期鏈接(每一個協議操做不會打開新鏈接)。一個客戶端庫鏈接使用單個TCP鏈接。服務器

  2.3 通道(Channels)
  創建完鏈接以後,客戶端會基於該TCP鏈接的基礎之上,開闢一條AMQP通道,客戶端執行的每一個協議操做都在通道上發生,通道存在於鏈接中,而不是單獨存在的。關閉鏈接後,其上的全部通道也將關閉。
  咱們也能夠在一條鏈接中開闢一條或多條AMQP通道,每條通道都會有一個惟一的ID來確保通道之間互不干預
網絡

  2.4 隊列(Queues)
  隊列是RabbitMQ服務器中消息的終點,相似於一個郵箱,能夠緩存消息;生產者向其中投遞消息,消費者從其中取出消息。負載均衡

  2.5 交換機(Exchanges)
  交換機用於接收生產者發送的消息,而且能處理消息,例如將消息遞交給某個隊列、遞交給全部隊列、或者將消息丟棄,它主要應用的有三種類型:
 a、Fanout:廣播(扇形),將消息交給全部綁定到交換機的隊列
 b、Direct:定向(直連),把消息交給符合指定routing_key 的隊列
 c、Topic:通配符(主題),把消息交給符合routing pattern(路由模式) 的隊列3d

補充:交換機只負責轉發消息,不具有存儲消息的能力,若是沒有任何隊列與Exchange綁定,或者沒有符合路由規則的隊列,那麼消息會丟失!

3.RabbitMQ的消息模式

  2.1 simple簡單模式

  生產者與消費者爲一對一的關係,一條消息只能被一個消費者消費,若是此時沒有消費者,那麼消息會被暫時緩存在queue中,直至被消費代理

  2.2 work工做模式

  在work模型下,生產者與消費者爲一對多的關係,多個消費者能夠綁定到一個隊列,共同消費隊列中的消息code

補充:消息一旦被消費者接收,隊列中的消息就會被刪除,全部不會存在消息重複消費的問題,即每條消息只能被多個消費者中的其中一個消費者消費
  問題1:在多個消費者的狀況下,消息如何分配

  queue能夠實現負載均衡,生產者將消息放入queue中後,RabbitMQ會經過輪詢的方式,實現消息的分配blog

  問題2:RabbitMQ怎麼知道消息被接收了呢

  經過消息確認機制(Acknowlege)實現。當消費者獲取消息後,會向RabbitMQ發送回執ACK,告知消息已經被接收。不過這種回執ACK分兩種狀況:rabbitmq

  • 自動ACK:消息一旦被接收,消費者自動發送ACK
  • 手動ACK:消息接收後,不會發送ACK,須要手動調用

    問題3:如何使用自動和手動ACK?

      這就要取決於消息的重要性,若是消息不過重要,丟失也沒有影響,那麼自動ACK會比較方便,反之,若是消息很是重要,不容丟失。那麼最好在消費完成後手動ACK,不然接收消息後就自動ACK,RabbitMQ就會把消息從隊列中刪除,若是此時消費者宕機,那麼消息就丟失了。

  2.3 訂閱模型-Fanout

  X表示交換機;在廣播模式下,消息發送流程是這樣的:

  • 1) 能夠有多個消費者
  • 2) 每一個消費者有本身的queue(隊列)
  • 3) 每一個隊列都要綁定到Exchange(交換機)
  • 4) 生產者發送的消息,只能發送到交換機,交換機來決定要發給哪一個隊列,生產者沒法決定。
  • 5) 交換機把消息發送給綁定過的全部隊列
  • 6) 隊列的消費者都能拿到消息。實現一條消息被多個消費者消費

  2.4 訂閱模型-Direct
在Direct模型下:

  • 1) 隊列與交換機的綁定,不能是任意綁定了,而是要指定一個routing_key(路由key)
  • 2) 消息的發送方在 向 Exchange發送消息時,也必須指定消息的 routing_key
  • 3) Exchange再也不把消息交給每個綁定的隊列,而是根據消息的routing_key進行判斷,只有隊列的routing_key與消息的 routing_key徹底一致,纔會接收到消息

    圖解:
  • P:生產者,向Exchange發送消息,發送消息時,會指定一個routing key。
  • X:Exchange(交換機),接收生產者的消息,而後把消息遞交給 與routing_key徹底匹配的隊列
  • C1:消費者,其所在隊列指定了須要routing_key 爲 error 的消息
  • C2:消費者,其所在隊列指定了須要routing_key 爲 info、error、warning 的消息

  2.5 訂閱模型-Topic
  Topic類型的Exchange與Direct相比,都是能夠根據routing_key把消息路由到不一樣的隊列。只不過Topic類型Exchange可讓隊列在綁定routing_key的時候使用通配符!通配符規則:
  #:能夠替代零個或多個單詞。
  *:能夠代替一個單詞

解釋:routing_key 通常都是有一個或多個單詞組成,多個單詞之間以」.」分割,例如: item.insert


圖解:

  • C1:消費者,其所在隊列Q1綁定了*.orange.*的routing_key,它能匹配到以orange爲中心,先後各一個單詞的路由,例如:item.orange.insert,可是該item.orange.insert.one路由就不能被匹配
  • C2:消費者,其所在隊列Q2綁定了lazy.#的routing_key,它能匹配到任何以lazy.#開頭的路由

    在上面交換機中,提到了消息丟失的問題,那麼哪一種狀況下消息會丟失,如何處理
  • 1) 生產者弄丟了數據。生產者將數據發送到 RabbitMQ 的時候,可能數據就在半路給搞丟了,好比:由於網絡問題。
    解決方案:使用發送方確認機制。發送方確認機制是指生產者將信道設置成confirm(確認)模式,一旦信道進入confirm模式,全部在該信道上面發佈的消息都會被指派一個惟一的ID(從1開始),一旦消息被投遞到RabbitMQ服務器以後,RabbitMQ就會發送一個確認(Basic.Ack)給生產者(包含消息的惟一ID),這就使得生產者知曉消息已經正確到達了目的地了。
  • 2) RabbitMQ 弄丟了數據。好比:MQ宕機了
    解決方案:RabbitMQ 的消息默認存放在內存上面,若是不特別聲明設置,消息不會持久化保存到硬盤上面的,若是MQ宕機,消息就會丟失。
    咱們能夠對消息進行持久化處理,要將消息持久化,前提是:隊列、Exchange都持久化
//獲取鏈接
Connection connection = ConnectionUtil.getConnection();
//創建通道
Channel channel = connection.createChannel();
//交換機持久化 ,第三個參數表明是否持久化
channel.exchangeDeclare(EXCHANGE_NAME,"EXCHANGE_TYPE",true);
//隊列持久化 ,第二個參數表明是否持久化
channel.queueDeclare(QUEUE_NAME,true,false,false,null);
//消息持久化 ,第三個參數表明是否持久化
channel.basicPublish(EXCHANGE_NAME,routing_key,MessageProperties.PERSISTENT_TEXT_PLAIN,MESSAGE_TEXT.getBYtes());
  • 3) 消費端弄丟了數據。剛接收到消息,還沒處理,結果進程掛了,好比:重啓了。 解決方案:使用消費者的ACK機制。當消費者獲取消息後,會向RabbitMQ發送回執ACK,告知消息已經被接收。若是這個消費者掛掉沒有發送應答,RabbitMQ會理解爲這個消息沒有被處理,而後交給另外一個消費者去從新處理。這樣,你就能夠確認即便消費者偶爾掛掉也不會丟失任何消息了。
相關文章
相關標籤/搜索