天天看點

RabbitMQ消息通信中間件中的那些概念了解rabbitmq的誕生環境設定與安裝消息通信的概念----生産者與消費者AMQP協定概念持久化總結

本章主要内容

  • 了解rabbitmq的誕生
  • 環境設定與安裝
  • AMQP協定
  • 消息通信概念-----生産者與消費者
  • 消息持久化

了解rabbitmq的誕生

        20世紀80年代,IBM、微軟等公司研發了商業級的MQ元件,但大多停留在金融行業等大型組織内部使用,因其價格昂貴,且不同供應商之間的MQ協定不能,不能直接互相通信,很多中小型公司無法使用這項技術。2004年,JPMorgan Chase需要一個更好的消息通信解決方案,并開始和iMatix公司一起合作開發Advanced Message Queuing Protocol(AMQP,進階消息隊列協定),它被設計成開放标準,以解決衆多的消息隊列需求和拓撲結構問題。随後,RabbitMq實作了AMPQ的特性,使其成為建構分布式應用的最完美的通信總線不二之選。

       今天,RabbitMQ并不是開放消息通信的唯一選擇,其它還有像ActiveMQ\ZeroMQ\apache Qpid\kafka等都提供了不同的開源消息隊列方案。相比之下RabbitMQ有什麼特點呢:

  • 除Qpid外,RabbitMQ是唯一實作了AMQP标準的代理伺服器
  • 正是使用了Erlang語言編寫的RabbitMQ,使得它配置叢集不可思議的簡單。
  • 更可靠,更能防止崩潰

環境設定與安裝

以window為例,先下載下傳安裝Erlang

http://www.erlang.org下載下傳最新的發行版,并配置環境變量

RabbitMQ消息通信中間件中的那些概念了解rabbitmq的誕生環境設定與安裝消息通信的概念----生産者與消費者AMQP協定概念持久化總結

http://www.rabbitmq.com上下載下傳最新的MQ安裝包(注意對應的系統),也同樣配置環境變量

RabbitMQ消息通信中間件中的那些概念了解rabbitmq的誕生環境設定與安裝消息通信的概念----生産者與消費者AMQP協定概念持久化總結
RabbitMQ消息通信中間件中的那些概念了解rabbitmq的誕生環境設定與安裝消息通信的概念----生産者與消費者AMQP協定概念持久化總結

下載下傳安裝到對應的目錄下,執行:

rabbitmq-server啟動MQ,可以在浏覽器中打開對應的管理界面,預設使用者與密碼都是guest

RabbitMQ消息通信中間件中的那些概念了解rabbitmq的誕生環境設定與安裝消息通信的概念----生産者與消費者AMQP協定概念持久化總結

至此,一個按預設配置的MQ就運作起來了。接下來,我們先了解一下rabbitmq的内部的構成元素有哪些,各起到什麼樣的作用。

消息通信的概念----生産者與消費者

        一般的通信,例如B/S或C/S構架都會有用戶端與服務端的概念,用戶端發送請求,服務端接收并處理請求。這種可以稱為快餐車模式,但Rabbitmq并不是這種模式,它更像是一種投遞服務模式,應用從rabbitmq中擷取的消息并不是rabbit産生的,就像是你收到的快遞并不是快遞員生産的一樣。是以,可以把rabbit當作是一種投遞服務,類似快遞員。在用戶端與伺服器之間扮演路由器的角色。是以應用程式連接配接到rabbit時就必須做成決定,是發送資料還是接收資料?從AMQP角度看,就是自己是一個生産者還是一個消費者。

生産者(producer)建立消息,然後釋出到代理伺服器(rabbit),消息包含兩部分:

  • 有效載荷(payload):就是你需要傳輸的資料,它可以是任何内容,一個字元串或一個JSON格式的資料或者是二進制流資料等等都可以。rabbitmq不關注這些
  • 标簽(label):它用來描述payload,并且rabbitmq用它來決定消息的流向,把消息發送給感興趣的一方。

這是一種“發後即忘”的單向方式。從上面可知:生産者會建立消息并設定标簽,然後交給rabbitmq代理伺服器。

消費者(consumer)連接配接到代理伺服器并且訂閱感興趣的消息隊列,每當消息到達此隊列時rabbitmq都會将其發送給其中一個訂閱者,當消息者接收到消息時,它隻得到消息的一部分,即:有效載荷部分,而消息标簽并沒有随消息一同投遞。甚至rabbit都不會告訴你生産者是誰。要想知道生産者是誰的唯一方式就是在消息裡(有效載荷中)簽名(在信封(envelope)上簽名)。

       可以看出這個過程很簡單,就是生産者建立消息,消息者接收處理消息。應用程式可以在這裡扮演生産者,同時也可以扮演消息者接收其它應用程式發送的消息。那麼消息是如何傳遞的呢?那就是通過信道傳遞的,什麼是信道?

信道(channel)

       無論是生産者還是消費者都必須先連接配接到rabbitmq代理伺服器,這個連接配接就是一個TCP連接配接,而信道就是建立在TCP連接配接内的虛拟連接配接。消息都是通過這些不同的信道投遞完成的,每個信道都會被指派一個唯一的ID辨別。我們有了TCP連接配接為什麼還是信道呢?主要原因就是對作業系統來說建立和銷毀TCP會話是非常高的開銷。假設隻使用TCP連接配接,那麼每個線程都需要自行連接配接到rabbit。在高峰時期可能會有上千條這樣的連接配接,不僅造成TCP連接配接的浪費而且作業系統每秒建立的TCP連接配接也是有限的。是以,這将成為一個瓶頸。如果我們為所有線程隻使用一條TCP連接配接以滿足性能方面的要求,但又能確定每個線程的私密性。這就是要引入信道概念的原因,線程啟動後,會在現成的連接配接上建立一條信道。是以,你可以每秒成百上千次地建立信道而不會影響作業系統。在一條TCP連接配接上建立多少條信道是沒有限制的。AMQP連接配接就像是一條光纜,信道就是光纖。

從總體上來說,消息通信,特别是AMQP,可以被當作加強版的傳輸層。使用信道,你能夠根據應用需要,盡可能多地建立并行的傳輸層,而不會被TCP連接配接限制所限制。當了解了這些概念後,你就可以把rabbitmq看作軟體的路由器了。

AMQP協定概念

AMQP消息路由包含三個部分:

  • 交換器
  • 隊列
  • 綁定

隊列

       生産者把消息發送到交換器後,消息最終到達隊列當中,它是AMQP消息通信的基礎子產品。為消息提供了處所,消息在此等待消費。而對負載均衡來說,隊列也是一個絕佳的方案,隻需要附加一堆消費者,并讓rabbitmq以循環的方式均勻地配置設定 發來的消息。它是消息的最後的終點。

      消息者通過AMQP的basic.consume指令訂閱隊列,當消息隊列中的消息後,将會自動接收下一條消息。如果消費者處理隊列消息時需要在消息一到達隊列後就自動接收處理,應該使用此指令訂閱。但如果隻想從隊列中獲得單條消息而不是持續訂閱的話可以使用basic.get指令擷取。有差別于basic.consume指令,不能使用循環basic.get代替它。get指令每次會訂閱消息,獲得單條消息,然後取消訂閱。是以性能方面考慮不使用循環。

        如果一個清單上沒有消費者,那麼消息将會在隊列中等待。直到有至少有一位消費者訂閱。當有一個訂閱者時,消息會立即發送給此訂閱者。但如果有多個訂閱者呢,隊列中的消息将如何發送?當有多個消費者時,隊列收到的消息将以循環的方式發送給消費者。每條消息隻會發送給一個訂閱的消費者。消費者接收到的每一條消息都必須進行确認,必須通過AMQP的basic.ack指令顯式地向rabbitmq發送一個确認,或者在訂閱到隊列時将auto_ack參數設定為true,此時一旦消費者接收消息,rabbitmq會自動視其确認了消息。當rabbitmq收到了确認接收消息後,才安全地把消息從隊列中删除。消費者對消息的确認和告訴生産者消息已經被接收了這兩件事沒有關系。但如果由于某些原因消費者沒有确認會發生什麼樣的事情呢?rabbit将不會給此消費者發送更多消息了。我們可以利用這個功能,當處理的消息非常耗時,可以延遲确認消息直到消息處理完成。可以防止rabbit持續不斷的消息湧向你的應用導緻過載。

      在rabbitmq2.0.0或更新的版本,增加了basic.reject指令,允許消費者拒絕消息,如果把reject指令的requeue參數設定成true的話,rabbitmq會将消息重新發送給下一個訂閱的消費者。如果設定成false的話,rabbitmq立即會把消息從隊列中移除。

隊列建立

       生産者和消費者都 可以使用AMQP的queue.declare指令來建立隊列,如果隊列已經被聲明,将不能被重複聲明。聲明時可以指定隊列的名稱,若不指定名稱,則會傳回一個随機名稱。還有其它可設定參數:

  • exclusive,如果為true的話,隊列将變成私有的,此時隻有你的應用程式才能消費隊列中的消息。
  • auto-delete,當最後一個消費者取消訂閱的時候,隊列就會自動移除。如果你需要臨時隊列隻為一個消費者服務的話,可以結合使用auto-delete和exclusive。當消費者斷開連接配接時,隊列就會被自動删除。
  • passive,為true,如果隊列已經存在那麼queue.declare指令會成功傳回,如果隊列不存在的話指令不會建立隊列而會傳回一個錯誤。

如果生産者發送的消息路由到一個不存在的隊列,則rabbit會忽略它們,即消息進入了“黑洞”不見了。

交換器(exchange)

       當你想要将消息投遞到隊列時,通過把消息發送給交換器來完成。然後,根據确定的規則,rabbitmq将會決定消息應該投遞到哪個隊列。這些規則被稱作路由鍵(routing key)隊列通過路由鍵綁定到交換器。空也是一種路由鍵。

rabbit會根據路由鍵将消息從交換器路由到隊列,協定中定義了不同類型的交換器。每種類型就是一種投遞方式:

  • direct(point to point),如果路由鍵routing-key與綁定到隊列上的binding-key比對的話就把消息投遞到對應的隊列。
  • fanout(multicast),這種類型的交換器會将收到的消息廣播到綁定的隊列上。當你發送一條消息到fanout交換器時,它會把消息投遞給所有附加在此交換器上的隊列。
  • topic(publish-subscribe),它可以使得來自不同源頭的消息能夠到達同一個隊列,“.”把路由鍵分為了幾部分,“*”比對特定位置的任意文本。為了實作比對所有規則,你可以使用“#”字元。
  • headers,允許比對AMQP消息的header而非路由鍵 。除此之外,headers交換器和direct交換器完全一緻,但性能會差很多。是以不太實用。

多租戶模式:虛拟主機與隔離

      vhost是AMQP概念的基礎,在連接配接到rabbit時必須進行指定。由于rabbitmq包含了開箱即用的預設vhost:"/",是以使用起來非常簡單。每個rabbitmq伺服器都能建立多個虛拟消息伺服器,其本質上是一個mini版本的rabbitmq伺服器。這樣很有用,它既能将同一rabbit的衆多客戶區分開來,又可以避免隊列和交換器的命名沖突。否則你可能不得不運作多個rabbit。相反,你可以隻運作一個rabbit,然後按需要啟動或關閉vhost。rabbit中的權限控制是以vhost為機關的。

當建立一個使用者時,使用者通常會被指派給至少一個vhost,并且隻能通路被指派vhost内的隊列、交換器和綁定。vhost之間是絕對隔離的,既保證了安全性,又確定了可移植性(當需要擴充的時候可以直接移植vhost到其它rabbit伺服器)。

持久化

持久化的對象有兩種

  • 交換器和隊列的持久化
    • 預設情況下,rabbit重新開機後它們将會消失。其原因就在于durable屬性,若為true則rabbit會把交換器或隊列持久化到磁盤。
  • 消息持久化
    • 若要持久化消息,就要在消息釋出前設定它的投遞模式(delivery mode)為持久化,而且前提條件必須是所到達的交換器和隊列也要持久化。

         rabbit確定消息能夠恢複的做法是,将消息寫入磁盤上的一個持久化日志檔案。當釋出一條持久化性的消息到交換器上時,rabbit會在消息送出到日志檔案後才發送響應。但如果此消息被路由到了一個非持久化的隊列上,它會自動從持久化日志中移除并且無法從rabbit重新開機中恢複。一旦消費了此持久化消息rabbit會在持久化日志中把這條消息标記為等待垃圾回收。鑒于持久化消息後,若發生當機等情況消息可以恢複的好處。但同時也要帶來性能上的問題,畢竟寫磁盤和操作記憶體的效率上相關還是很大的。那麼,如何考慮消息是否需要被持久化呢?

       權衡取舍,需要先分析應用對性能的需求,如果需要單台rabbit處理100000+/s的消息,那麼持久化将是最大的一個瓶頸,需要考慮使用其它方式來確定消息投遞的正确性,例如:使用更快速的存儲系統。或者另開一個信道當消費者消費了消息後在規定的時間内通知生産者,若生産者未收到确認消費的資訊,将會重發,以確定消息不會丢失。rabbit能幫助確定投遞,但這也不是萬無一失的。硬碟崩潰等原因,也有可能使得持久化消息的丢失。最終確定消息安全到達都将取決于你的政策。

       另一個與持久化相關的是AMQP的事務,把信道設定為事務模式後,發送那些想要确認的消息,可以發送多條AMQP指令,這些指令是執行帶是忽略取決于第一條消息發送是否成功。如果成功,将會執行其它的指令,若投遞不成功将不會執行。雖然事務是AMQP0-9-1規範的一部分,但使用它幾乎将rabbit的性能吸幹。是以可以使用其它的方式來代替事務,例如:發送方确認模式。

       與事務相仿,需要要先把信道設定成confirm模式,而且你隻能通過重新建立信首來關閉此設定。一旦進入了confirm模式,所有在信道上釋出的消息都會被指派一個ID号。當消息被投遞完成後,信道會發送一個發送方确認模式給生産者。這樣生産者就知曉消息已經安全到達隊列中了。如果消息和隊列都是持久化的,那麼會在寫入磁盤後才會發出确認。它是異步的,如果rabbit發生投遞錯誤,将會發送一條nack未确認的消息,生産者可以根據此消息來決定是重新處理還是其它。此模式,由于沒有消息復原的概念,更加輕量級,同時也對rabbit伺服器的性能影響也可以忽略不計。

總結

本章主要是了解rabbitmq中的相關元素,及各元素之間是如何協同工作的,消息是怎樣被安全投遞的等等。了解這些基本的概念後,就會對rabbitmq的整個工作流程有一個清楚的認識。在應用程式編碼時先幹什麼,後幹什麼,是否需要持久化等等也會有一個清晰的思路。