李甲蔚

你创想,云实现

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

什么是DeadLetterQueue

DeadLetterQueue的概念不仅仅存在于Service Bus Brokered Messaging 服务中,在各种平台的消息队列中间件中,都包含DeadLetterQueue的概念。DeadLetterQueue是在正常的消息队列之外,由系统生成的一个子队列,用来存储“有问题”的消息。这个“有问题”的消息,可能是指发送端发送失败的消息,或者是接收方处理失败的消息,或者是超期未处理的消息。

DeadLetterQueue in Service Bus Brokered Messaging

在Service Bus Brokered Messaging中,每一个Queue与Subscription都包含一个子队列用以实现DeadLetterQueue,这个子队列由Service Bus系统生成,如图所示:

在以下几种情况中,Message将会被移入DeadLetterQueue:

  1. 消息接收方,在消息被Complete和Abandon之前,显式调用Message的DeadLetter方法,可将消息移入DeadLetter队列;
  2. Queue或Subscription的EnableDeadLetteringOnMessageExpiration设置为true,当消息过期以后,将自动被移入DeadLetter队列;
  3. Subscription的EnableDeadLetteringOnFilterEvaluationExceptions属性设置为true,当消息过滤出现异常时,将自动被移入DeadLetter队列;
  4. 当消息的接收次数超过MaxDeliveryCount设置的值时,将自动被移入DeadLetter队列;

显式调用DeadLetter

什么样的情况需要显示调用DeadLetter方法将消息移入DeadLetter队列呢?我考虑了一个场景,假设接收方是一个订单处理系统,发送方将订单信息包含在消息中进行发送,但是这个接收方只能处理固定区域的订单,对于其他区域的订单无法处理,这时,如果接收到其他区域的订单,则可以暂不处理并将其移入DeadLetter队列,等正常的消息处理完成以后,再来对DeadLetter队列中的订单消息做一些特殊处理。

发送消息:

   1: string queueName = "MyQueue";
   2: NamespaceManager namespaceClient = NamespaceManager.Create();
   3: if (namespaceClient.QueueExists(queueName))
   4: {
   5:     namespaceClient.DeleteQueue(queueName);
   6: }
   7: QueueDescription queueDescription = new QueueDescription(queueName);
   8: namespaceClient.CreateQueue(queueDescription);
   9:  
  10: MessagingFactory factory = MessagingFactory.Create();
  11: QueueClient queueClient = factory.CreateQueueClient(queueName);
  12: CreateAndSendOrderMessage(Guid.NewGuid().ToString(), "Beijing", queueClient);
  13: CreateAndSendOrderMessage(Guid.NewGuid().ToString(), "Dalian", queueClient);
  14: CreateAndSendOrderMessage(Guid.NewGuid().ToString(), "Guangzhou", queueClient);
  15:  
  16: Console.WriteLine();
  17: Console.WriteLine("Press [Enter] to delete queue and exit.");
  18: Console.ReadLine();
  19: namespaceClient.DeleteQueue(queueName);
  20: factory.Close();
  21: queueClient.Close();
   1: private static void CreateAndSendOrderMessage(string orderId, string orderRegion, QueueClient sender)
   2: {
   3:     var message = new BrokeredMessage();
   4:     message.Properties.Add("OrderId", orderId);
   5:     message.Properties.Add("OrderRegion", orderRegion);
   6:     Console.WriteLine("Sending message of order region {0}.", message.Properties["OrderRegion"]);
   7:     sender.Send(message);
   8: }

 

接收消息:

   1: string queueName = "MyQueue";
   2: MessagingFactory factory = MessagingFactory.Create();
   3: QueueClient queueClient = factory.CreateQueueClient(queueName, ReceiveMode.PeekLock);
   4: Console.WriteLine("Reading messages from queue...");
   5: BrokeredMessage message = null;
   6: while ((message = queueClient.Receive(TimeSpan.FromSeconds(2))) != null)
   7: {
   8:     string orderRegion = message.Properties["OrderRegion"].ToString();
   9:     if (validatedRegion.Contains(orderRegion))
  10:     {
  11:         Console.WriteLine("Process the message whose order region is {0}", orderRegion);
  12:         ProcessMessage(message);
  13:         message.Complete();
  14:     }
  15:     else
  16:     {
  17:         Console.WriteLine("DeadLetter the message whose order region is {0}", orderRegion);
  18:         message.DeadLetter("Invalid Region", string.Format("The order region {0} is invalid", orderRegion));
  19:     }
  20:     message.Dispose();
  21: }
  22: queueClient.Close();

处理DeadLetter队列中的消息:

   1: string deadLetterQueuePath = QueueClient.FormatDeadLetterPath(queueName);
   2: QueueClient deadletterQueueClient = factory.CreateQueueClient(deadLetterQueuePath, ReceiveMode.PeekLock);
   3: Console.WriteLine("Reading messages from deadletter queue...");
   4:  
   5: BrokeredMessage messageInDeadletterQueue = null;
   6: while ((messageInDeadletterQueue = deadletterQueueClient.Receive(TimeSpan.FromSeconds(2))) != null)
   7: {
   8:     LogDeadletterMessage(messageInDeadletterQueue);
   9:     messageInDeadletterQueue.Complete();
  10:     messageInDeadletterQueue.Dispose();
  11: }
  12:  
  13: deadletterQueueClient.Close();

过期消息

假设我们在创建队列或者Subscription时,将默认的消息存活时间设置为5s(当然也可以给每个消息单独设置TimeToLive),并且将EnableDeadLetteringOnMessageExpiration设置为true,如果为false,则过期消息将被直接删除。

   1: string queueName = "MyQueue";
   2: NamespaceManager namespaceClient = NamespaceManager.Create();
   3: if (namespaceClient.QueueExists(queueName))
   4: {
   5:     namespaceClient.DeleteQueue(queueName);
   6: }
   7: QueueDescription queueDescription = new QueueDescription(queueName)
   8: {
   9:     DefaultMessageTimeToLive = TimeSpan.FromSeconds(5),
  10:     EnableDeadLetteringOnMessageExpiration = true
  11: };
  12: namespaceClient.CreateQueue(queueDescription);

那在消息发送以后,等5s我们再去正常接收消息,会发现没有任何消息,消息因超期被移入了DeadLetter队列中,接收消息代码同显示调用DeadLetter。测试结果如图所示:

消息发送:

消息接收:

接收次数超过MaxDeliveryCount值

我们将Queue或Subscription的MaxDeliveryCount属性设置为2,那么一旦消息被接收的次数到达2次以后将会被移入DeadLetter队列。

   1: string queueName = "MyQueue";
   2: NamespaceManager namespaceClient = NamespaceManager.Create();
   3: if (namespaceClient.QueueExists(queueName))
   4: {
   5:     namespaceClient.DeleteQueue(queueName);
   6: }
   7: QueueDescription queueDescription = new QueueDescription(queueName)
   8: {
   9:     MaxDeliveryCount = 2
  10: };
  11: namespaceClient.CreateQueue(queueDescription);

接收消息:

   1: string queueName = "MyQueue";
   2: MessagingFactory factory = MessagingFactory.Create();
   3: QueueClient queueClient = factory.CreateQueueClient(queueName, ReceiveMode.PeekLock);
   4: Console.WriteLine("Reading messages from queue...");
   5: BrokeredMessage message = null;
   6: while ((message = queueClient.Receive(TimeSpan.FromSeconds(2))) != null)
   7: {
   8:     Console.WriteLine("Order Id:{0}", message.Properties["OrderId"]);
   9:     message.Abandon();
  10:     message.Dispose();
  11: }
  12: queueClient.Close();

从DeadLette队列中收取消息代码同上。

测试结果:

从图中我们可以看到,总共3个订单消息,每个消息被接收了两次,然后从DeadLetter队列中就能收取到这3条消息。

Filter Evaluation Exception

快崩溃了,一直没能模拟出Filter Evaluation Exception,所以无法为大家提供例证。后期我再补全并上传代码。哪位好心朋友如果清楚Filter Evaluation Exception如何模拟请给我留言,谢谢。

posted on 2013-01-23 14:25  李甲蔚  阅读(1620)  评论(0编辑  收藏  举报