Azure Messaging-ServiceBus 메시지 퀴즈: 복잡한 객체 메시지의 시리얼리제이션 및 메시지 퍼시스티언

Azure Messaging-ServiceBus 메시지 퀴즈 시리즈의 4번째 글로, 이전 글에서 메시지의 순서 보장에 대해 다루었습니다. 이번 글에서는 복잡한 객체를 메시지에 포함시킬 때 시리얼리제이션과 메시지 퍼시스티언이 필요하다는 주제를 다룹니다. 실제 애플리케이션 개발에서는 복잡한 비즈니스 객체를 메시지에 담아 다른 시스템 간 통합이나 모듈 간의 결합도를 낮추는 등의 목적으로 사용합니다. 또한, 메시지 퀴즈 서비스가 메시지를 영속화하는지에 대한 관심도 큽니다. 만약 메시지 퀴즈 서비스가 중단되면 영속화된 메시지는 복원될 수 있을까요? Azure Messaging의 공식 문서에서는 복잡한 객체 메시지에 시리얼리제이션을 강요하지 않습니다. 그러나 이전 글에서와 같이 BrokeredMessage 클래스의 생성자에 대해 살펴보면, 다음과 같이 동적으로 XmlObjectSerializer를 전달할 수 있는 구조를 제공합니다:

public BrokeredMessage(object serializableObject, XmlObjectSerializer serializer);

다음은 메시지를 생성하는 방법示例입니다:

public BrokeredMessage Create(Object serializableObject)
{
    var serializer = new DataContractSerializer(serializableObject.GetType(),
        new DataContractSerializerSettings() { IgnoreExtensionDataObject = true, PreserveObjectReferences = true });
    var message = new BrokeredMessage(serializableObject, serializer);
    message.Properties.Add("Type", serializableObject.GetType().ToString());

    return message;
}

이전 글의 코드를 사용하여 복잡한 객체 메시지를 보내고 받는 테스트를 수행합니다. 이전에 사용한 SalesOrder 클래스를 사용하며 양방향 연결을 갖는 SalesOrderItem 컬렉션을 추가합니다. 먼저 SalesOrder 클래스는 다음과 같습니다:

public class SalesOrder
{
    public string OrderID { get; set; }
    public string Code { get; set; }
    public DateTime CreateTime { get; set; }
    public Decimal TotalPrice { get; set; }
    public int ProductID { get; set; }

    private List<SalesOrderItem> items;

    public List<SalesOrderItem> Items
    {
        get
        {
            if (items == null)
                items = new List<SalesOrderItem>();
            return items;
        }
        set
        {
            items = value;
        }
    }
}

SalesOrderItem 클래스는 다음과 같습니다:

public class SalesOrderItem
{
    public string ID { get; set; }
    public int CustomerID { get; set; }
    public string SalesOrderID
    {
        get
        {
            if (Order != null)
                return Order.OrderID;
            return string.Empty;
        }
    }
    public SalesOrder Order { get; set; }
}

SalesOrder 인스턴스를 생성하는 방법은 다음과 같습니다:

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 간에 양방향 연결을 설정했습니다. 메시지 보내기 및 받기 테스트 코드는 다음과 같습니다:

class Program
{
    private static readonly string queueName = "OrderQueue";
    static void Main(string[] args)
    {
        MessageSend();
        Console.ReadKey();

        MessageReceive();
        Console.ReadKey();
    }

    private static void MessageSend()
    {
        var sbUtils = new ServiceBusUtils();

        sbUtils.CreateQueue(queueName, false);

        var queueSendClient = sbUtils.GetQueueClient(queueName);
        for (int i = 0; i < 10; i++)
        {
            var order = CreateSalesOrder(i);
            var message = sbUtils.Create(order);
            queueSendClient.Send(message);
            Console.WriteLine(string.Format("메시지 보내기: 인덱스 {0}, MessageID: {1}", i, message.MessageId));
        }

        Console.WriteLine("메시지 보내기 완료!");
    }

    private static void MessageReceive()
    {
        int index = 0;
        BrokeredMessage msg = null;
        var sbUtils = new ServiceBusUtils();
        var queueReveiveClient = sbUtils.GetReceiveQueueClient(queueName, ReceiveMode.ReceiveAndDelete);
        while ((msg = queueReveiveClient.Receive(TimeSpan.FromMilliseconds(3))) != null)
        {
            Console.WriteLine(string.Format("메시지 수신: 인덱스 {0}, MessageID: {1}", index, msg.MessageId));
            index++;
        }

        Console.WriteLine("메시지 수신 완료!");
    }

    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;
    }
}

위 코드에서 볼 수 있듯이,適當한 XmlObjectSerializer를 지정하면 복잡한 객체 메시지를 손쉽게 전송할 수 있습니다. 양방향 참조를 갖는 도메인 모델의 경우, PreserveObjectReferences를 true로 설정하여 반복 참조 문제를 해결할 수 있습니다.

var serializer = new DataContractSerializer(serializableObject.GetType(),
    new DataContractSerializerSettings() { IgnoreExtensionDataObject = true, PreserveObjectReferences = true });

Azure Messaging의 공식 문서에 따르면, 모든 퀴즈는 영속화됩니다. 영속화 저장소는 SQL Server이며, 메모리 기반의 퀴즈는 제공하지 않습니다. 이번 글에서는 Azure Messaging Service Bus에서 복잡한 객체 메시지가 시리얼리제이션과 메시지 영속화를 지원하는지에 대해 설명하고 테스트를 통해 확인했습니다. 다음 글에서는 메시지의 중복 발송 문제에 대해 다루겠습니다.

태그: Azure.Messaging ServiceBus MessageQueue serialization Persistance

7월 25일 06:54에 게시됨