Back to Blogs
rabbitmq

RabbitMQ 中 Message Persistence and Durability

如何透過 Durable Queue、Persistent Message 與 Publisher Confirms 三者搭配,確保訊息在 Broker 重啟後不遺失

Aaron

Message 在 RabbitMQ 有哪些情況會丟失呢?

  1. producer 根本沒有正確的把 Message 丟到 Exchange
  2. 當 Queue 重啟消失後, 裡面的 Message 自然會丟失
  3. 當 Queue durable 設定 true, 重啟後 Queue 還在, 但是 Message 預設只寫到記憶體, 所以重啟後 Queue 中的 Message 丟失
  4. 如果 fsync 寫入磁碟之前 crush, 那麼等於 message 丟失
  5. Consumer 有沒有正常接收到呢? 如果 RabbitMQ 以為 Message 正常接收到丟失了
  6. Consumer nack/reject 該 Message, 但是沒有 DLX / requeue 該 Message 就丟失了

lose-message

How to Solve ?

Producer 端

要如何確保 Message 有正確被 RabbitMQ 接收到並且成功寫入磁碟呢? 這有兩個問題:

  1. Exchange 有沒有 Binding 到任何 Queue ? 因為儲存訊息是在 Queue 不是 Exchange
  2. Message 到 Queue 有沒有正確寫入磁碟呢?
await channel.publish(EXCHANGE, "order.created.vip.asia", buffer, {
  persistent: true, // 設定 Message 進到 Queue 後要寫入磁碟
  mandatory: true, // 要求「至少一個 queue 收到,否則退回」
}) 

channel.on("return", (msg) => {
  // 當 mandatory:true, 然後沒有 Queue binding 則會退 Message 回 Producer, 需要 listen basic.return 去處理
})

// ack: 代表 代表 broker 已**完整處理**。對 persistent message 來說 ack 代表訊息已寫入磁碟(fsync 完成),queue durable 的話 broker 重啟也不丟
// nack: 代表 broker 收到但無法處理——通常是 broker 內部問題(磁碟寫入失敗、資源耗盡、異常狀態), 這時候就會 raise error
await chan.waitForConfirms() 

Queue 端

  1. 如何確保 Queue 重啟後會存在呢?
// 設定 Queue durable 為 true
await channel.assertQueue(QUEUE, { durable: true });

Consumer 端

  1. 如何確保 Consumer 有正確拿到 Message ?
  2. 如果 Consumer nack 該如何處理?

// 設定 Queue DLX 確保有處理 requeue false 的 message
await channel.assertQueue(QUEUE, { durable: true, deadLetterExchange: 'deadLetterExchange' });

const { consumerTag } = await channel.consume(QUEUE, async (msg: ConsumeMessage | null) => {
    try {
      process(msg)
      channel.ack(msg);
    } catch (err) {
      // 需不需要 requeue 重試一遍
      // 不需要則進入 DLX 去處理
      channel.nack(msg, false, !msg.fields.redelivered);
    }
  }, 
  // 關掉 autoAck, 當 Consumer ack 後才會把 Queue 中的 Message 移除掉
  { noAck: false }
);

總結

Durability 是靠整條鏈的設定來保證

  • Producer → broker:Publisher Confirms + mandatory flag
  • Broker 寫入磁碟:durable queue + persistent message
  • 主 queue → DLX:沒有 publisher confirm,只能靠正確配置 + 測試 + 監控
  • Broker → consumer:manual ack