6.【RabbitMQ實戰】- 死信隊列

概念

死信,顧名思義就是無法被消費的消息,字面意思可以這樣理解,一般來說,producer 將消息投遞到 broker 或者直接到queue 裏了,consumer 從 queue 取出消息進行消費,但某些時候由於特定的原因導致 queue 中的某些消息無法被消費,這樣的消息如果沒有後續的處理,就變成了死信,有死信自然就有了死信隊列。

應用場景

爲了保證訂單業務的消息數據不丟失,需要使用到 RabbitMQ 的死信隊列機制,當消息 消費發生異常時,將消息投入死信隊列中.還有比如說: 用戶在商城下單成功並點擊去支付後在指定時 間未支付時自動失效

死信的來源

  • 消息TTL過期
  • 隊列達到最大長度(隊列滿了,無法添加數據到mq中)
  • 消息被拒絕(basic.reject或basic.nack)並且requeue = false

image.png

消息TTL過期

生產者代碼

image.png

消費者代碼

Consumer1代碼

using rabbitmq.common;

using RabbitMQ.Client;
using RabbitMQ.Client.Events;

using System.Text;

namespace Exchange.Consumer1
{
    public class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("C1 開始接消息");
            using var channel = RabbitmqUntils.GetChannel();

            /*
              申明一個 test_exchange 交換機
              配置轉發到死信隊列參數
              綁定交換機與對列
              
             */
            channel.ExchangeDeclare(RabbitmqUntils.test_exchange, "direct",false,false,null);
            var arguments = new Dictionary<string, object> { };
            arguments.Add("x-dead-letter-exchange", "dead_exchange");//正常隊列設置死信交換機 參數 key 是固定值
            arguments.Add("x-dead-letter-routing-key", "dead");//正常隊列設置死信 routing-key 參數 key 是固定值
            channel.QueueDeclare(RabbitmqUntils.test_queue, false,false,false, arguments);
            channel.QueueBind(RabbitmqUntils.test_queue, RabbitmqUntils.test_exchange, RabbitmqUntils.test_routingkey, null);

            /*
             申明一個死信交換機
             綁定交換機與對列
             */
            channel.ExchangeDeclare(RabbitmqUntils.dead_exchange, "direct", false, false, null);            
            channel.QueueDeclare("dead_queue", false, false, false, null);
            channel.QueueBind(RabbitmqUntils.dead_queue, RabbitmqUntils.dead_exchange, RabbitmqUntils.dead_routingkey, null);


            //事件對象
            var consumer = new EventingBasicConsumer(channel);
            channel.BasicConsume(queue: RabbitmqUntils.test_queue, true, consumer);
            consumer.Received += (sender, e) =>
            {
                var body = e.Body.ToArray();
                var message = Encoding.UTF8.GetString(body);
                Console.WriteLine("C1 接收消息: {0}", message);
            };

            Console.ReadKey();
        }
    }
}

Consumer2代碼

using rabbitmq.common;

using RabbitMQ.Client;
using RabbitMQ.Client.Events;

using System.Text;

namespace Exchange.Consumer2
{
    public class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("C2 開始接消息");
            using var channel = RabbitmqUntils.GetChannel();

            /*
               申明一個死信交換機
               綁定交換機與對列
            */
            channel.ExchangeDeclare(RabbitmqUntils.dead_exchange, "direct", false, false, null);
            channel.QueueDeclare("dead_queue", false, false, false, null);
            channel.QueueBind(RabbitmqUntils.dead_queue, RabbitmqUntils.dead_exchange, RabbitmqUntils.dead_routingkey, null);

            //事件對象
            var consumer = new EventingBasicConsumer(channel);
            channel.BasicConsume(queue: "dead_queue", true, consumer);
            consumer.Received += (sender, e) =>
            {
                var body = e.Body.ToArray();
                var message = Encoding.UTF8.GetString(body);
                Console.WriteLine("C2 接收消息: {0}", message);
            };

            Console.ReadKey();
        }
    }
}

測試效果

  • 先啓動C1 創建交換機,隊列和交換機與對列之間的綁定關係

image.png

  • 此時關閉C1,模擬C1無法正常消費
  • 啓動生產者發送10條數據, 可在test_queue先有10條數據,10s後test_queue10條數據消息,已將10條數據轉發到dead_queue

image.png
image.png

  • 最後啓動C2 消費死信隊列中的數據

image.png

隊列達到最大長度

生產者去掉設置ttl
image.png
C1 添加代碼
image.png

啓動C1之前先刪除隊列否則會出現一下錯誤:
image.png

測試效果

由於設置了隊列做多6個消息所以剩下的4個消息被C2消費了
image.png
image.png

消息被拒絕

C1代碼調整

去掉設置長度限制代碼並刪除隊列重新啓動C1
image.png
調整爲手動應答 autoAck:false
image.png

模擬消息1被拒絕
image.png

測試效果

image.png
image.png
image.png
image.png

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章