在上一篇中,我們介紹了消息的順序收發保證:
Azure Messaging-ServiceBus Messaging消息隊列技術系列3-消息順序保證
在本文中我們主要介紹下複雜對象消息是否需要支援序列化以及消息的持久化。
在實際的業務應用開發中,我們經常會将複雜業務對象放到消息裡面,實作異構系統之間的內建、子產品間的解耦等等。
同時,我們還比較關注消息隊列服務是否支援消息的持久化,消息隊列如果當機後持久化的消息是否可以還原?
在Azure Messaging的官方說明中,沒有特地的介紹複雜對象消息是否需要支援序列化的要求,但是,我們在上篇博文中,有個消息建立方法,as following,
BrokeredMessage類的構造函數:
//
// Summary:
// Constructor that creates a BrokeredMessage from a given object using the
// provided XmlObjectSerializer
//
// Parameters:
// serializableObject:
// The serializable object.
//
// serializer:
// The serializer object.
//
// Exceptions:
// System.ArgumentNullException:
// Thrown when null serializer is passed to the method with a non-null serializableObject
//
// Remarks:
// You should be aware of the exceptions that their provided Serializer can
// throw and take appropriate actions. Please refer to for a possible list of
// exceptions and their cause.
public BrokeredMessage(object serializableObject, XmlObjectSerializer serializer);
看來消息的構造,支援動态傳入XmlObjectSerializer, so,
1 /// <summary>
2 /// 構造消息
3 /// </summary>
4 /// <param name="serializableObject">可序列化的對象</param>
5 /// <returns>消息</returns>
6 public BrokeredMessage Create(Object serializableObject)
7 {
8 var serializer = new DataContractSerializer(serializableObject.GetType(),
9 new DataContractSerializerSettings() { IgnoreExtensionDataObject = true, PreserveObjectReferences = true });
10 var message = new BrokeredMessage(serializableObject, serializer);
11 message.Properties.Add("Type", serializableObject.GetType().ToString());
12
13 return message;
14 }
接下來,我們用上一篇中的代碼,做一個複雜對象消息收發的測試,我們還是用上次的SalesOrder類,但是增加一個SalesOrderItem集合和雙向關聯,來描述銷售訂單和銷售訂單明細的的1:n的業務領域模型。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AzureMessaging.FIFO
{
/// <summary>
/// 銷售訂單類
/// </summary>
public class SalesOrder
{
/// <summary>
/// 訂單ID
/// </summary>
public string OrderID { get; set; }
/// <summary>
/// 訂單編号
/// </summary>
public string Code { get; set; }
/// <summary>
/// 建立時間
/// </summary>
public DateTime CreateTime { get; set; }
/// <summary>
/// 總價格
/// </summary>
public Decimal TotalPrice { get; set; }
/// <summary>
/// 産品ID
/// </summary>
public int ProductID { get; set; }
private List<SalesOrderItem> items;
/// <summary>
/// 銷售訂單明細
/// </summary>
public List<SalesOrderItem> Items
{
get
{
if (items == null)
items = new List<SalesOrderItem>();
return items;
}
set
{
items = value;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AzureMessaging.FIFO
{
/// <summary>
/// 銷售訂單明細
/// </summary>
public class SalesOrderItem
{
/// <summary>
/// 辨別
/// </summary>
public string ID { get; set; }
/// <summary>
/// 客戶ID
/// </summary>
public int CustomerID { get; set; }
/// <summary>
/// 所屬的銷售訂單ID
/// </summary>
public string SalesOrderID
{
get
{
if (Order != null)
return Order.OrderID;
return string.Empty;
}
}
/// <summary>
/// 所屬的銷售訂單
/// </summary>
public SalesOrder Order { get; set; }
}
}
建立銷售訂單執行個體類方法:
private static SalesOrder CreateSalesOrder(int i)
{
var order = new SalesOrder() { OrderID = i.ToString(), Code = "SalesOrder_" + i, CreateTime = DateTime.Now, ProductID = 17967, TotalPrice = new decimal(19999) };
order.Items.Add(new SalesOrderItem() { ID = Guid.NewGuid().ToString(), Order = order, CustomerID = 1234567 });
return order;
}
在構造SalesOrder和SalesOrderItems時,我們做了雙向關聯。
消息順序收發測試:
1 using Microsoft.ServiceBus.Messaging;
2 using System;
3 using System.Collections.Generic;
4 using System.Linq;
5 using System.Text;
6 using System.Threading.Tasks;
7
8 namespace AzureMessaging.FIFO
9 {
10 class Program
11 {
12 private static readonly string queueName = "OrderQueue";
13 static void Main(string[] args)
14 {
15 MessageSend();
16 Console.ReadKey();
17
18 MessageReceive();
19 Console.ReadKey();
20 }
21
22 /// <summary>
23 /// 發送消息
24 /// </summary>
25 private static void MessageSend()
26 {
27 var sbUtils = new ServiceBusUtils();
28
29 //建立隊列
30 sbUtils.CreateQueue(queueName, false);
31
32 //順序發送消息到OrderQueue
33 var queueSendClient = sbUtils.GetQueueClient(queueName);
34 for (int i = 0; i < 10; i++)
35 {
36 var order = CreateSalesOrder(i);
37 var message = sbUtils.Create(order);
38 queueSendClient.Send(message);
39 Console.WriteLine(string.Format("Send {0} MessageID: {1}", i, message.MessageId));
40 }
41
42 Console.WriteLine("Send Completed!");
43 }
44
45 /// <summary>
46 /// 接收消息
47 /// </summary>
48 private static void MessageReceive()
49 {
50 int index = 0;
51 BrokeredMessage msg = null;
52 var sbUtils = new ServiceBusUtils();
53 var queueReveiveClient = sbUtils.GetReceiveQueueClient(queueName, ReceiveMode.ReceiveAndDelete);
54 while ((msg = queueReveiveClient.Receive(TimeSpan.FromMilliseconds(3))) != null)
55 {
56 Console.WriteLine(string.Format("Received {0} MessageID: {1}", index, msg.MessageId));
57 index++;
58 }
59
60 ////删除隊列
61 //sbUtils.DeleteQueue(queueName);
62
63 Console.WriteLine("Receive Completed!");
64 }
65
66 private static SalesOrder CreateSalesOrder(int i)
67 {
68 var order = new SalesOrder() { OrderID = i.ToString(), Code = "SalesOrder_" + i, CreateTime = DateTime.Now, ProductID = 17967, TotalPrice = new decimal(19999) };
69 order.Items.Add(new SalesOrderItem() { ID = Guid.NewGuid().ToString(), Order = order, CustomerID = 1234567 });
70
71 return order;
72 }
73 }
74 }
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcukzNxkTN5QTN4ETL4QjM4UjN0EjMwEzMwcTMwITL1ITNzIzLcNDM3EDMy8CX1ITNzIzLcd2bsJ2Lc12bj5ycn9Gbi52YuUTMwIzcldWYtl2Lc9CX6MHc0RHaiojIsJye.png)
可以看出,複雜對象消息隻要指定适當的XmlObjectSerializer,即可。
在雙向引用這種領域模型的設計場景下,我們配置了PreserveObjectReferences = true
var serializer = new DataContractSerializer(serializableObject.GetType(),
new DataContractSerializerSettings() { IgnoreExtensionDataObject = true, PreserveObjectReferences = true });
解決了序列化時循環引用的問題。
關于消息的持久化,Azure messaging有官方的說明:所有的隊列都是持久化的,持久化存儲是SQL Server,不提供記憶體中的消息隊列。
畢竟是PaaS層的消息隊列服務,消息的持久化和高可用性微軟還是有保障的。
本篇中我們介紹并驗證了Azure Messaging Service Bus複雜對象消息是否需要支援序列化和消息持久化,下一篇我們繼續介紹消息的重複發送問題。
周國慶
2017/3