李甲蔚

你创想,云实现

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

在Service Bus Queue 和Topics/Subscriptions中,发送的消息内容都需要封装成BrokeredMessage对象,接收方收到的消息也是BrokeredMessage对象实例。所以,充分理解BrokeredMessage对象的使用是深入Service Bus brokered messaging服务的前提。这篇随笔将从BrokeredMessage的结构、如何创建、属性以及方法的使用全面介绍一下BrokeredMessage。

BrokeredMessage结构

如图所示,一个消息包含消息头和消息体,整个消息的容量上限是256K。

消息头

消息头包含了一些固定的属性,比如MessageId,SessionId等,以及一个键值对用来管理自定义的属性。消息头的大小不能超过64K。

消息体

消息体包含了需要传输的序列化的数据,可以是一个业务对象,或者是数据流,比如FileStream,也可以为空。

创建BrokeredMessage对象

构造方法

Name Description
BrokeredMessage() 创建BrokeredMessage的新实例
BrokeredMessage(Object) 创建BrokeredMessage的新实例,传入对象默认使用DataContractSerializer 与XmlDictionaryWriter binary编码进行序列化(这种序列化方式为WCF标准的序列化方式,Service Bus其实与WCF有着千丝万缕的联系,并且整个Windows Azure的实现中WCF都无处不在,以后我们会看到这一点),序列化后的数据流作为消息体存储并传输。
BrokeredMessage(Stream, Boolean) 创建BrokeredMessage的新实例,传入参数Stream作为消息体进行传输。
BrokeredMessage(Object, XmlObjectSerializer) 创建BrokeredMessage的新实例,并使用提供的序列化器对传入对象进行序列化。

Public method

举两个例子:

使用对象构造消息:

   1: IssueData issueData = new IssueData()
   2: {
   3:     IssueID = 1,
   4:     IssueTitle = "Title"
   5: };
   6: BrokeredMessage message = new BrokeredMessage(issueData);

接收消息:

   1: BrokeredMessage message = myQueueClient.Receive(TimeSpan.FromSeconds(1));
   2: if (message != null)
   3: {
   4:     IssueData issueData = message.GetBody<IssueData>();
   5:     message.Complete();
   6: }

使用Stream构造消息:

   1: FileStream photo = new FileStream(@"E:\image027.png", FileMode.Open);
   2: BrokeredMessage msg = new BrokeredMessage(photo, true);
   3: myQueueClient.Send(msg);

接收消息:

   1: BrokeredMessage message = myQueueClient.Receive(TimeSpan.FromSeconds(1));
   2: if (message != null)
   3: {
   4:     try
   5:     {
   6:         Stream fs = message.GetBody<Stream>();
   7:         message.Complete();
   8:     }
   9:     catch
  10:     {
  11:         message.Abandon();
  12:     }
  13: }

 

BrokeredMessage常用方法

Name Description
Abandon 当消息接收模式是PeekLock时,调用此方法将解锁消息,解锁后消息重新回到Queue或Subscription中,其他客户端可以接收该消息。
Complete 当消息被接收方成功处理以后,调用此方法将消息从队列中删除,其他客户端无法获取该消息。
DeadLetter 调用DeadLetter方法将消息放置到DeadLetterQueue中。在使用PeekLock模式接收消息,并且消息没有被Abandon或是Complete,此方法可调用。后期将有关于DeadLetter的具体介绍,请关注我的随笔。
Defer 当消息接收模式是PeekLock时,调用此方法将终止消息的处理,并且将其“挂起”,随后通过消息的SequenceNumber再将defered的消息取回并进行处理。通过Defer方法可以调整消息处理的优先级,比如餐厅,对于外卖服务和送餐服务,一般情况下外卖比送餐具有更高的优先级,这会可以只处理外卖服务消息,同时将收到的送餐消息Defer,Defer之前需要将消息的SequenceNumber存储,等外卖服务消息处理完成以后,再根据SequenceNumber将之前Defered的送餐服务消息取回,并进行处理,保证了外卖服务的优先处理。
GetBody<T> 调用此泛型方法将获得消息体,消息体反序列化成T类型对象输出。

 

BrokeredMessage常用属性

BrokeredMessage类提供了一系列的属性用于在应用中实现特定的消息处理逻辑。所有的属性存储在消息头部,并且不需要经过序列化。如果数据量小的话,我们可以考虑将数据存储在属性键值对中(BrokeredMessage.Properties),因不需要序列化,这么做可以改善性能。

MessageId

MessageId用来唯一标识一个Message,当创建BrokeredMessage时,MessageId将自动获得一个Guid值,我们可以在消息发送前根据需求修改MessageId的值。

MessageId通常用在检测重复消息机制中,比如,消息内容为订单信息,如果要实现业务规则规定订单号一致的消息不能重复发送,

首先需要在创建Queue的时候将RequiresDuplicateDetection属性设为true(请注意一定是在创建的时候,不支持创建完成后再修改属性)

   1: QueueDescription myQueue = null;
   2: myQueue = namespaceClient.CreateQueue(new QueueDescription(queueName) { RequiresDuplicateDetection = true });

然后使用订单号作为BrokeredMessage的MessageId,这时同样订单号的消息虽然能发送成功,假设发送了3条消息拥有同样的订单号,但是在接收方只能接收到一条(实际上Queue中也是只包含一条信息,虽然调用QueueClient得Send方法成功,但是实际并没有发送到Queue中),避免了消息的重复处理。

Label

Label属性值为字符串类型,发送方和接收方可以用来实现一些应用层特定的业务逻辑。我们可以理解为Label相当于一个标记,接收方可以根据这个标记来判断消息的特性,从而确定该如何处理消息。

   1: // Create a brokered message based on the order.
   2: BrokeredMessage orderInMsg = new BrokeredMessage(orderIn);
   3: // Use the labelproperty to indicate that this is a test message.
   4: orderInMsg.Label = "TestMessage";

Properties

Properties值类型是IDictionary<string, Object>,可以往里头存储一一对应的键值对。在Service Bus Topics/Subscriptions入门这篇文章中,提到了使用Properties对发送至Subscription的消息进行过滤。另外,如果使用简单业务对象作为消息内容,且数据量较小(消息头最大64K),我们也可以通过Properties来存储消息内容,避免对象序列化造成的性能损失。

   1: // Create a message from the order.
   2: BrokeredMessage orderMsg = new BrokeredMessage(order);
   3:  
   4: // Set message properties from values in the order.
   5: orderMsg.Properties.Add("loyalty", order.HasLoyltyCard);
   6: orderMsg.Properties.Add("items", order.Items);
   7: orderMsg.Properties.Add("value", order.Value);
   8: orderMsg.Properties.Add("region", order.Region);

CorrelationId

CorrelationId属性类型为字符串,其同样可以作为Subscription的过滤条件,使得Subscription只接收CorrelationID为某一特定值的消息。

   1: // Create a message from the order.
   2: BrokeredMessage orderMsg = new BrokeredMessage(order);
   3:  
   4: // Set the CorrelationId to the region.
   5: orderMsg.CorrelationId = order.Region
   1: string correlationId = "China";
   2: CorrelationFilter filter = new CorrelationFilter(correlationId);
   3: SubscriptionDescription subscriptionDescription = namespaceClient.CreateSubscription(topicName, subscriptionName, filter);

TimeToLive

TimeToLive属性定义了消息在云端的过期时间,默认消息永不过期,即消息发送到了Queue中,只要没有被删除,总是能被接收方接收。我们可以通过这个属性自定义消息的过期时间。

   1: // Create a message from the order.
   2: BrokeredMessage orderMsg = new BrokeredMessage(order);
   3:  
   4: // Set the message time to live to 30 days.
   5: orderMsg.TimeToLive = TimeSpan.FromDays(30);

SessionId

SessionId属性类型为字符串,当我们需要相同的接收方接收一批消息的时候,可以使用SessionId。

假设有这么一种情况,消息接收方在进行消息处理时并不是一条一条处理的,而是需要10条消息作为一个整体来处理,则如果仍旧每次接收一条消息,则有可能在接收到第5条时,下一条数据被其他接收方获取了,那就破坏了整体性。基于这种情况,我们可以这10条消息赋值相同的SessionId,表明这10条消息属于同一会话,是一个不可分割的整体。接收方通过AcceptMessageSession获取这10条消息的会话(MessageSession),此时会话无法被其他接收方获取,最后接收方通过MessageSession将这10条消息获取下来,并作出统一处理。(请注意如需使用SessionId,需要在创建Queue的时候将RequiresSession属性设置为true。后续将有针对MessageSession更详细介绍文章,欢迎关注)

   1: List<PizzaOrder> Orders = OrderManager.GetPendingOrders();
   2:  
   3: string orderBatchId = Guid.NewGuid().ToString();
   4:  
   5: foreach (PizzaOrder order in Orders)
   6: {
   7:     // Create a brokered message based on the order.
   8:     BrokeredMessage orderMsg = new BrokeredMessage(orderIn);
   9:  
  10:     // Set the SessionId.
  11:     orderMsg.SessionId = orderBatchId;
  12:  
  13:     // Send the message.
  14:     queueClient.Send(orderMsg);
  15: }

ScheduledEnqueueTimeUtc

ScheduledEnqueueTimeUtc设置了消息的可见时间,消息发送以后在哪个时刻能够被接收方接收。我想大概的应用场景可以是这样,对于客户的提前预定,比如预定了周日上午十点的金融服务,我们可以开发一个提醒服务,应用程序在收到这个预定时,将提醒消息发送出去,并且告知Service Bus在周日上午九点半才让这个提醒消息可见,这样的话接收方可以提前半小时获得这个提醒,然后为客户的到来做准备。

   1: // Create a new pizza order.
   2: PizzaOrder orderIn = new PizzaOrder()
   3: {
   4:     Name = "Alan",
   5:     Pizza = "Hawaiian",
   6:     Quantity = 1
   7: };
   8:  
   9: // Create a brokered message based on the order.
  10: BrokeredMessage orderInMsg = new BrokeredMessage(orderIn);
  11:  
  12: // Schedule the order.
  13: orderInMsg.ScheduledEnqueueTimeUtc = DateTime.Parse("2013-01-27 09:30");
  14:  
  15: // Send the message to the queue.
  16: queueClient.Send(orderInMsg);

Size

Size是一个只读属性,提供的消息的大小,单位是byte。在使用Size的时候,有一点需要特别注意,即在消息发送前,通过Size获取到的仅仅是消息体的大小,而在消息发送之后,通过Size获取到的才是消息头+消息体的大小,即消息的真实大小。

posted on 2013-01-22 15:45  李甲蔚  阅读(2106)  评论(1编辑  收藏  举报