1. 程式人生 > >RabbitMQ 延時訊息設計

RabbitMQ 延時訊息設計

 問題背景

  1. 所謂"延時訊息"是指當訊息被髮送以後,並不想讓消費者立即拿到訊息,而是等待指定時間後,消費者才拿到這個訊息進行消費。
  2. 場景一:客戶A在十二點下了一個訂單,我想半個小時後來檢查一下這個訂單的付款狀態,根據付款狀態來作下一步的處理。 a. 針對場景一,建議採用方案資料庫儲存+schedule的方式也許更合適。
  3. 場景二:mdc系統更新了一個A資訊,我要通知給A門店資訊發生了變化,通知他們回撥API來讀取最新的值。

        如果拿到訊息後立即回撥,可能因為mdc事務、快取、從庫延遲等原因,拿到變化前的資訊,所以

mdc希望能延遲一段時間再來消費此訊息。

目標

  1. 可以實現訊息按照自定義的時間延遲傳送。
  2. 最好做到對訊息生產者和消費者透明,不修改現有應用程式。

總體方案

     3.1. 實現原理

  1. AMQP和RabbitMQ本身沒有直接支援延遲佇列功能,但是可以通過以下特性模擬出延遲佇列的功能。
  2. RabbitMQ可以針對Queue和Message設定 x-message-ttl,來控制訊息的生存時間,如果超時,則訊息變為dead letter
  3. RabbitMQ的Queue可以配置x-dead-letter-exchange  x-dead-letter-routing-key(可選)兩個引數,用來控制佇列內出現了
    dead letter,則按照這兩個引數重新路由。

結合以上兩個特性,就可以模擬出延遲訊息的功能

        參考資料:

  1. https://www.cloudamqp.com/docs/delayed-messages.html
  2. http://www.rabbitmq.com/ttl.html
  3. http://www.rabbitmq.com/dlx.html
  4. http://www.rabbitmq.com/maxlength.html

   3.2. 技術方案

 

方案一:針對Queue設定延遲時間

方案二:針對Message設定延遲時間

 

 

 

 

 

方案關鍵點

針對需要延遲的Queue配置ttl引數

針對Queue設定最大延遲時間

 

 

1. 優點:

傳送方對每個Message設定有效延遲時間

 

 

a. 維護簡單

 

 

 

b. 客戶端完全透明

1. 優點

 

 

c. 針對每個延遲時間建立一個延遲隊

a. 傳送時可以自定義延遲時間

 

 

2. 缺點

 

 

2. 缺點:

a. 需要升級客戶端,對客戶端不透明

 

 

a. 傳送方無法自定義延遲時間

b. 需要針對每個佇列建立不同的延遲

 

 

b. 延遲時間在建Queue時確定,修改

佇列

 

 

不便

c. 帶延遲引數的send方法容易誤用,

 

 

c. 修改延遲時間需要在MQ叢集重新進

很難發現

 

 

行配置

 

 

選擇結果

最終選擇了方案一

 

 

 

3.3. 訊息傳送流程

      3.3.1. 原有業務流程

 

     3.3.2. 自產自消的延遲訊息流程

 

    3.3.3. 普通的延遲訊息流程

 

4. 操作步驟

   4.1. 建立 delay.exchange

注意事項:

1. 不要設定為Internal,否則將無法接受dead letter

 

    4.2. 建立延時佇列(delay queue)

注意事項:

  1. 按照延期時間配置queue的名字,建議命名規則為delay.{time}.queue,使用delay字首方便排序和做一些許可權控制
  2. cos線上叢集預設配置了delay.1m.queue、delay.5m.queue、delay.15m.queue三個佇列
  3. 通過TTL設定佇列的延期時間,對應不同的Queue
  4. MAX length為最大的積壓的訊息個數,推薦設定為100w~500w之間,推測方法見積壓測試結果,
  5. 超過MAX length限制以後,佇列頭部的訊息會立即轉到delay.exchange進行投遞,不會造成訊息丟失
  6. 設定dead letter exchange為剛才配置的 delay.exchange

注意不要配置"dead letter routing key"否則會覆蓋掉訊息傳送時攜帶的routingkey,導致後面無法路由

 

   4.3. 配置延時路由規則

       4.3.1. 需要延時的訊息到exchange後先路由到指定的延時佇列

     

   4.3.2. delay.exchange 再重新把訊息路由到正常的queue或exchang中

       4.3.3. 消費者和以前一樣從正常queue中接收消費訊息

 5. 積壓訊息測試結果

    5.1. 積壓測試結論

      從實現原理上看,對於持久化訊息,記憶體主要儲存的是訊息的索引資料,從測試結果也可以驗證,可以得出以下資料:

  1. 記憶體佔用方面估算
    1. 訊息佔用記憶體的大小確實和訊息體本身大小無關,和訊息個數直接相關。
      1. 訊息體為40位元組的字串,積壓10萬訊息,佔用101MB記憶體,23萬訊息,佔用230MB記憶體
      2. 訊息體為一個整數,積壓7萬訊息,佔用64MB記憶體,24萬訊息,佔用240MB記憶體
    2. 一個訊息約佔1KB的記憶體,以10GB記憶體,留一半餘量:5GB/1K=500 0000
    3. 線上單個queue推薦線上的max length不要超過500萬,根據延遲時間和訊息量來調整此值的大小。
  2. 訊息量估算
    1. 30分鐘延遲,每秒傳送1000個訊息為例,則最大值為:30*60*1000=180 0000
    2. 倒推:以500萬,30分鐘延遲為例,則每秒最多傳送的訊息個數為:5000000/30/60=2777
  3. 訊息積壓數超過max length以後,訊息不會丟失,只會導致訊息會被提前消費。

結論:1. MAX length推薦這個值設定為100萬~500萬,既可以滿足業務需求,又不超過記憶體限制

   

    5.2. 40位元組訊息積壓26萬

   

  5.3. 4位元組訊息積壓7萬

   

 

   5.4. 4位元組訊息積壓24萬

 

   5.5. 4位元組訊息積壓31萬

   

   5.6. 4位元組訊息積壓64萬

總結:經過壓測及基礎配置,延時訊息可以滿足目前的需求,實現訊息的延遲處理。

 不知道延遲佇列加上惰性佇列的組合,即可以減小記憶體佔用,又可以實現訊息的延遲處理,也是一個非常好的方案。蛋糕和咖啡真的很配。