.net core 下使用 RabbitMQ 失败重试 (七)

主要 代码 

消息的 Properties.headers.x-death 属性中查询到消息投递源信息和消息被投递的次数;
 1 
 2             var consumer = new EventingBasicConsumer(channel);
 3             consumer.Received += (model, ea) =>
 4             {
 5                 var _message = (BasicDeliverEventArgs)ea;                //消息传送参数
 6                 var _headers = _message.BasicProperties.Headers;        //消息头
 7                 List<object> xdeaths = (List<object>)_headers["x-death"];
 8                 Dictionary<string, object> result = (Dictionary<string, object>)xdeaths[0];
 9                 long count = (long)result["count"];
10 
11                 var body = ea.Body.ToArray();
12                 var message = Encoding.UTF8.GetString(body);
13                 Console.WriteLine(ea.RoutingKey);
14                 Console.WriteLine(" [x] Received {0}", message);
15             };

 

核心方法

  • BasicAck 用于确认当前消

  • BasicNack 用于拒绝当前消息,可以执行批量拒绝

  • BasicReject 用于拒绝当前消息,仅拒绝一条消息

   消息拒收 查看下面文章

(21条消息) .Net Core&RabbitMQ消息转发可靠机制(上)_寒冰屋的博客-CSDN博客

 


 

 

 

  1 using RabbitMQ.Client;
  2 using RabbitMQ.Client.Events;
  3 using System;
  4 using System.Collections.Generic;
  5 using System.Linq;
  6 using System.Text;
  7 using System.Threading.Tasks;
  8 
  9 namespace RabbitMQ_Consumer.Dead
 10 {
 11     /// <summary>
 12     /// 什么是消息确认机制?
 13     /// MQ消息确认类似于数据库中用到的 commit 语句,用于告诉broker本条消息是被消费成功了还是失败了;
 14     /// 平时默认消息在被接收后就被自动确认了,需要在创建消费者时、设置 autoAck: false 即可使用手动确认模式;
 15     /// ====================================================================================
 16     /// 什么是死信队列?
 17     /// 死信队列是用于接收普通队列发生失败的消息,其原理与普通队列相同;
 18     /// > 失败消息如:被消费者拒绝的消息、TTL超时的消息、队列达到最大数量无法写入的消息;
 19     /// 死信队列创建方法:
 20     /// > 在创建普通队列时,在参数"x-dead-letter-exchange"中定义失败消息转发的目标交换机;
 21     /// > 再创建一个临时队列,订阅"x-dead-letter-exchange"中指定的交换机;
 22     /// > 此时的临时队列就能接收到普通队列失败的消息了;
 23     /// > 可在消息的 Properties.headers.x-death 属性中查询到消息投递源信息和消息被投递的次数;
 24     /// </summary>
 25     public class DeadExchange
 26     {
 27         private static string _exchangeNormal = "Exchange.Normal";  //定义一个用于接收 正常 消息的交换机
 28         private static string _exchangeRetry = "Exchange.Retry";    //定义一个用于接收 重试 消息的交换机
 29         private static string _exchangeFail = "Exchange.Fail";      //定义一个用于接收 失败 消息的交换机
 30         private static string _queueNormal = "Queue.Noraml";        //定义一个用于接收 正常 消息的队列
 31         private static string _queueRetry = "Queue.Retry";          //定义一个用于接收 重试 消息的队列
 32         private static string _queueFail = "Queue.Fail";            //定义一个用于接收 失败 消息的队列
 33 
 34         public static void TestDemo()
 35         {
 36             var connection = RabbitMQHelper.GetConnection();
 37             var channel = connection.CreateModel();
 38 
 39             //声明交换机
 40             channel.ExchangeDeclare(exchange: _exchangeNormal, type: "topic");
 41             channel.ExchangeDeclare(exchange: _exchangeRetry, type: "topic");
 42             channel.ExchangeDeclare(exchange: _exchangeFail, type: "topic");
 43 
 44             //定义队列参数
 45             var queueNormalArgs = new Dictionary<string, object>();
 46             {
 47                 // 绑定失败交互交换机
 48                 queueNormalArgs.Add("x-dead-letter-exchange", _exchangeFail);   //指定死信交换机,用于将 Noraml 队列中失败的消息投递给 Fail 交换机
 49             }
 50             var queueRetryArgs = new Dictionary<string, object>();
 51             {
 52                 queueRetryArgs.Add("x-dead-letter-exchange", _exchangeNormal);  //指定死信交换机,用于将 Retry 队列中超时的消息投递给 Noraml 交换机
 53                 queueRetryArgs.Add("x-message-ttl", 6000); //定义 queueRetry 的消息最大停留时间 (原理是:等消息超时后由 broker 自动投递给当前绑定的死信交换机)                                                                             
 54                 //定义最大停留时间为防止一些 待重新投递 的消息、没有定义重试时间而导致内存溢出
 55             }
 56             var queueFailArgs = new Dictionary<string, object>();
 57             {
 58                 //暂无
 59             }
 60 
 61             //声明队列
 62             channel.QueueDeclare(queue: _queueNormal, durable: true, exclusive: false, autoDelete: false, arguments: queueNormalArgs);
 63             channel.QueueDeclare(queue: _queueRetry, durable: true, exclusive: false, autoDelete: false, arguments: queueRetryArgs);
 64             channel.QueueDeclare(queue: _queueFail, durable: true, exclusive: false, autoDelete: false, arguments: queueFailArgs);
 65 
 66             //为队列绑定交换机
 67             channel.QueueBind(queue: _queueNormal, exchange: _exchangeNormal, routingKey: "#");
 68             channel.QueueBind(queue: _queueRetry, exchange: _exchangeRetry, routingKey: "#");
 69             channel.QueueBind(queue: _queueFail, exchange: _exchangeFail, routingKey: "#");
 70 
 71             #region 创建一个普通消息消费者
 72             {
 73                 var consumer = new EventingBasicConsumer(channel);
 74 
 75                 consumer.Received += (sender, e) =>
 76                 {
 77                     var _sender = (EventingBasicConsumer)sender;            //消息传送者
 78                     var _channel = _sender.Model;                           //消息传送通道
 79                     var _message = (BasicDeliverEventArgs)e;                //消息传送参数
 80                     var _headers = _message.BasicProperties.Headers;        //消息头
 81                     var _content = Encoding.UTF8.GetString(_message.Body.ToArray());  //消息内容
 82                     var _death = default(Dictionary<string, object>);       //死信参数
 83 
 84                     if (_headers != null && _headers.ContainsKey("x-death"))
 85                         _death = (Dictionary<string, object>)(_headers["x-death"] as List<object>)[0];
 86 
 87                     try
 88                     #region 消息处理
 89                     {
 90                         Console.WriteLine();
 91                         Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")}\t(1.0)消息接收:\r\n\t[deliveryTag={_message.DeliveryTag}]\r\n\t[consumerID={_message.ConsumerTag}]\r\n\t[exchange={_message.Exchange}]\r\n\t[routingKey={_message.RoutingKey}]\r\n\t[content={_content}]");
 92 
 93                         throw new Exception("模拟消息处理失败效果。");
 94 
 95                         //处理成功时
 96                         Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")}\t(1.1)处理成功:\r\n\t[deliveryTag={_message.DeliveryTag}]");
 97 
 98                         //消息确认 (销毁当前消息)
 99                         _channel.BasicAck(deliveryTag: _message.DeliveryTag, multiple: false);
100                     }
101                     #endregion
102                     catch (Exception ex)
103                     #region 消息处理失败时
104                     {
105                         var retryCount = (long)(_death?["count"] ?? default(long)); //查询当前消息被重新投递的次数 (首次则为0)
106 
107                         Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")}\t(1.2)处理失败:\r\n\t[deliveryTag={_message.DeliveryTag}]\r\n\t[retryCount={retryCount}]");
108 
109                         if (retryCount >= 2)
110                         #region 投递第3次还没消费成功时,就转发给 exchangeFail 交换机
111                         {
112                             //消息拒绝(投递给死信交换机,也就是上边定义的 ("x-dead-letter-exchange", _exchangeFail))
113                             _channel.BasicNack(deliveryTag: _message.DeliveryTag, multiple: false, requeue: false);
114                         }
115                         #endregion
116                         else
117                         #region 否则转发给 exchangeRetry 交换机
118                         {
119                             var interval = (retryCount + 1) * 10; //定义下一次投递的间隔时间 (单位:秒)
120                                                                   //如:首次重试间隔10秒、第二次间隔20秒、第三次间隔30秒
121 
122                             //定义下一次投递的间隔时间 (单位:毫秒)
123                             _message.BasicProperties.Expiration = (interval * 1000).ToString();
124 
125                             //将消息投递给 _exchangeRetry (会自动增加 death 次数)
126                             _channel.BasicPublish(exchange: _exchangeRetry, routingKey: _message.RoutingKey, basicProperties: _message.BasicProperties, body: _message.Body);
127 
128                             //消息确认 (销毁当前消息)
129                             _channel.BasicAck(deliveryTag: _message.DeliveryTag, multiple: false);
130                         }
131                         #endregion
132                     }
133                     #endregion
134                 };
135                 channel.BasicConsume(queue: _queueNormal, autoAck: false, consumer: consumer);
136             }
137             #endregion
138 
139             #region 创建一个失败消息消费者
140             {
141                 var consumer = new EventingBasicConsumer(channel);
142 
143                 consumer.Received += (sender, e) =>
144                 {
145                     var _message = (BasicDeliverEventArgs)e;                //消息传送参数
146                     var _content = Encoding.UTF8.GetString(_message.Body.ToArray());  //消息内容
147 
148                     Console.WriteLine();
149                     Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")}\t(2.0)发现失败消息:\r\n\t[deliveryTag={_message.DeliveryTag}]\r\n\t[consumerID={_message.ConsumerTag}]\r\n\t[exchange={_message.Exchange}]\r\n\t[routingKey={_message.RoutingKey}]\r\n\t[content={_content}]");
150                 };
151 
152                 channel.BasicConsume(queue: _queueFail, autoAck: true, consumer: consumer);
153             }
154             #endregion
155 
156             Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")}\t正在运行中...");
157 
158             var cmd = default(string);
159             while ((cmd = Console.ReadLine()) != "close")
160             #region 模拟正常消息发布
161             {
162                 var msgProperties = channel.CreateBasicProperties();
163                 var msgContent = $"消息内容_{DateTime.Now.ToString("HH:mm:ss.fff")}_{cmd}";
164 
165                 channel.BasicPublish(exchange: _exchangeNormal, routingKey: "亚洲.中国.经济", basicProperties: msgProperties, body: Encoding.UTF8.GetBytes(msgContent));
166 
167                 Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")}\t发送成功:{msgContent}");
168                 Console.WriteLine();
169             }
170             #endregion
171 
172             Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")}\t正在关闭...");
173 
174             channel.ExchangeDelete(_exchangeNormal);
175             channel.ExchangeDelete(_exchangeRetry);
176             channel.ExchangeDelete(_exchangeFail);
177             channel.QueueDelete(_queueNormal);
178             channel.QueueDelete(_queueRetry);
179             channel.QueueDelete(_queueFail);
180             //channel.Abort();
181             channel.Close(200, "Goodbye!");
182             channel.Dispose();
183             connection.Close(200, "Goodbye!");
184             connection.Dispose();
185 
186             Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")}\t运行结束。");
187             Console.ReadKey();
188         }
189     }
190 }

 

posted on 2023-02-01 00:28  是水饺不是水饺  阅读(107)  评论(1编辑  收藏  举报

导航