RabbitMQ系列三 (深刻消息隊列)

消息持久化是 RabbitMQ 最爲人津津樂道的特性之一, RabbitMQ 可以在付出最小的性能代價的基礎上實現消息的持久化,最大的奧祕就在於 RabbitMQ 多層消息隊列的設計上。下面,本文就從 MessageQueue 的設計和消息在 MessageQueue 的生命週期兩個方面全面介紹  RabbitMQ 的消息隊列。

   RabbitMQ徹底實現了AMQP協議,相似於一個郵箱服務。Exchange負責根據ExchangeTypeRoutingKey將消息投遞到對應的消息隊列中,消息隊列負責在消費者獲取消息前暫存消息。在RabbitMQ中,MessageQueue主要由兩部分組成,一個爲AMQQueue,主要負責實現AMQP協議的邏輯功能。另一個是用來存儲消息的BackingQueue,本文重點關注的是BackingQueue的設計。 性能

   RabbitMQ系列三 (深刻消息隊列) - 網易杭研後臺技術中心 - 網易杭研後臺技術中心的博客

    RabbitMQBackingQueue又由5個子隊列組成:Q1Q2DeltaQ3Q4RabbitMQ中的消息一旦進入隊列,不是固定不變的,它會隨着系統的負載在隊列中不斷流動,消息的狀態不斷髮生變化。RabbitMQ中的消息一共有5種狀態: spa

   a)Alpha:消息的內容和消息索引都保存在內存中; 設計

   b)Beta:消息內容保存在磁盤上,消息索引保存在內存中; 索引

   c)Gamma:消息內容保存在磁盤上,消息索引在磁盤和內存都有; 生命週期

   d)Delta:消息內容和索引都在磁盤上; 隊列

   注意:對於持久化的消息,消息內容和消息索引都必須先保存到磁盤上,纔會處於上述狀態中的一種,而Gamma狀態的消息只有持久化的消息纔會有該狀態。 內存

      BackingQueue 中的 5 個子隊列中的消息狀態, Q1 Q4 對應的是 Alpha 狀態, Q2 Q3 Beta 狀態, Delta 對應的是 Delta 狀態。上述就是 RabbitMQ 的多層隊列結構的設計,咱們能夠看出從 Q1 Q4 ,基本經歷的是由 RAM DISK ,再到 RAM 的設計。這樣的設計的好處就是當隊列負載很高的狀況下,可以經過將一部分消息由磁盤保存來節省內存空間,當負載下降的時候,這部分消息又漸漸回到內存,被消費者獲取,使得整個隊列有很好的彈性。下面咱們就來看一下,整個消息隊列的工做流程。
     引發消息流動主要有兩方面的因素:其一是消費者獲取消息;其二是因爲內存不足,引發消息的換出到磁盤上( Q1-.>Q2 Q2->Delta Q3->Delta Q4->Q3 )。 RabbitMQ 在系統運行時會根據消息傳輸的速度計算一個當前內存中可以保存的最大消息數量( Target_RAM_Count ),當內存中的消息數量大於該值時,就會引發消息的流動。進入隊列的消息,通常會按着 Q1->Q2->Delta->Q3->Q4 的順序進行流動,可是並非每條消息都必定會經歷全部的狀態,這個取決於當時系統的負載情況。
       當消費者獲取消息時,首先會從 Q4 隊列中獲取消息,若是 Q4 獲取成功,則返回,若是 Q4 爲空,則嘗試從 Q3 獲取消息;首先,系統會判斷 Q3 隊列是否爲空,若是爲空,則直接返回隊列爲空,即此時隊列中無消息(後續會論證)。若是不爲空,則取出 Q3 的消息,而後判斷此時 Q3 Delta 隊列的長度,若是都爲空,則可認爲 Q2 Delta Q3 Q4 所有爲空 ( 後續說明 ) ,此時將 Q1 中消息直接轉移到 Q4 中,下次直接從 Q4 中獲取消息。若是 Q3 爲空, Delta 不空,則將 Delta 中的消息轉移到 Q3 中;若是 Q3 非空,則直接下次從 Q3 中獲取消息。在將 Delta 轉移到 Q3 的過程當中, RabbitMQ 是按照索引分段讀取的,首先讀取某一段,直到讀到的消息非空爲止,而後判斷讀取的消息個數與 Delta 中的消息個數是否相等,若是相等,則判定此時 Delta 中已無消息,則直接將 Q2 和剛讀到的消息一併放入 Q3 中。若是不相等,則僅將這次讀到的消息轉移到 Q3 中。這就是消費者引發的消息流動過程。
    RabbitMQ系列三 (深刻消息隊列) - 網易杭研後臺技術中心 - 網易杭研後臺技術中心的博客
     下面咱們分析一下因爲內存不足引發的消息換出。消息換出的條件是內存中保存的消息數量 + 等待 ACK 的消息的數量 >Target_RAM_Count 。當條件觸發時,系統首先會判斷若是當前進入等待 ACK 的消息的速度大於進入隊列的消息的速度時,會先處理等待 ACK 的消息。步驟基本上 Q1->Q2 或者 Q3 移動,取決於 Delta 隊列是否爲空。 Q4->Q3 移動, Q2 Q3 Delta 移動。
    最後,咱們來分析一下前面遺留的兩個問題,一個是爲何 Q3 隊列爲空便可認定整個隊列爲空。試想若是 Q3 爲空, Delta 不空,則在 Q3 取出最後一條消息時, Delta 上的消息就會被轉移到 Q3 上,與 Q3 空矛盾。若是 Q2 不空,則在 Q3 取出最後一條消息,若是 Delta 爲空時,會將 Q2 的消息併入 Q3 ,與 Q3 爲空矛盾。若是 Q1 不空,則在 Q3 取出最後一條消息,若是 Delta Q3 均爲空時,則將 Q1 的消息轉移到 Q4 中,與 Q4 爲空矛盾。這也解釋了另一個問題,即爲何 Q3 Delta 爲空, Q2 就爲空。
    上述就是整個消息在 RabbitMQ 隊列中流動過程。從上述流程能夠看出,消息若是可以被儘早消費掉,就不須要經歷持久化的過程,由於這樣會加系統的開銷。若是消息被消費的速度過慢, RabbitMQ 經過換出內存的方式,防止內存溢出。
相關文章
相關標籤/搜索