Как получить тип сообщения очереди

Я использую очереди хранилища Azure и хочу написать код, который извлекает все очереди, а затем находит обработчик, который может обрабатывать сообщение в этой очереди. Для этого я определил такой интерфейс:

public interface IHandler<T>

У меня есть несколько реализаций этого интерфейса, например: IHandler<CreateAccount> или IHandler<CreateOrder>. Я использую 1 очередь для каждого типа сообщений, поэтому сообщения CreateAccount помещаются в очередь create-account-queue.

Как мне их подключить? Чтобы найти правильный класс Handler для сообщения, мне сначала нужно знать тип сообщения, но кажется, что объекты CloudQueueMessage не содержат этой информации.


person Leon Cullens    schedule 13.04.2017    source источник


Ответы (2)


На самом деле это не ответ на ваш вопрос, но я расскажу, как мы справляемся с той же ситуацией в нашем приложении.

В нашем приложении мы отправляем различные типы сообщений, как и вы, и обрабатываем эти сообщения в фоновом процессе.

Что мы делаем, так это включаем тип сообщения в само тело сообщения. Итак, наше сообщение обычно выглядит так:

        message: {
          type: 'Type Of Message',
          contents: {
            //Message contents
          }
        }

Одно ключевое отличие состоит в том, что все сообщения помещаются в одну очередь (вместо разных очередей в вашем случае). Получатель (фоновый процесс) просто опрашивает одну очередь, получает сообщение и соответственно определяет тип сообщения и обработчик вызова для этого сообщения.

person Gaurav Mantri    schedule 13.04.2017
comment
Это было то, о чем я тоже думал. Может быть, вы могли бы даже указать тип в заголовках? У меня есть одна проблема: что произойдет, если вы реорганизуете свой код и переименуете класс? IDE не скажет вам, что код сломается, но, тем не менее, сломается. - person Leon Cullens; 13.04.2017
comment
Maybe you could even put the type in the headers? - Не могли бы вы объяснить, что вы имеете в виду под Headers здесь? Я согласен с вашим комментарием, но если вы сериализуете объект на стороне отправителя и десериализуете его для создания того же объекта на стороне получателя, вы можете свести к минимуму риск этого. - person Gaurav Mantri; 13.04.2017
comment
На самом деле я думал, что у объектов CloudQueueMessage также есть заголовки (полезные для хранения таких вещей, как, сколько раз вы уже пытались обработать сообщение и откуда оно пришло), но, по-видимому, у них нет заголовков. - person Leon Cullens; 13.04.2017

Вы можете связать метаданные с каждой очередью. Поскольку вы упомянули, что используете одну очередь для каждого типа сообщения, вы можете поместить имя обработчика в метаданные для каждой очереди. Затем вы можете перечислить все очереди и получить метаданные для каждой очереди, которые сообщают вам, какой тип обработчика вы должны использовать. Вот быстрое консольное приложение, которое демонстрирует то, что, как я думаю, вы просите:

using System;
using System.Collections.Generic;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Queue; 

namespace QueueDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //get a ref to our account.
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse("UseDevelopmentStorage=true;");
            CloudQueueClient cloudQueueClient = storageAccount.CreateCloudQueueClient();

            //create our queues and add metadata showing what type of class each queue contains. 
            CloudQueue queue1 = cloudQueueClient.GetQueueReference("queue1"); 
            queue1.Metadata.Add("classtype", "classtype1");
            queue1.CreateIfNotExists();

            CloudQueue queue2 = cloudQueueClient.GetQueueReference("queue2");
            queue2.Metadata.Add("classtype", "classtype2");
            queue2.CreateIfNotExists();

            //enumerate our queues in a storage account and look at their metadata...
            QueueContinuationToken token = null;
            List<CloudQueue> cloudQueueList = new List<CloudQueue>();
            List<string> queueNames = new List<string>();

            do
            {
                QueueResultSegment segment = cloudQueueClient.ListQueuesSegmented(token);
                token = segment.ContinuationToken;
                cloudQueueList.AddRange(segment.Results);
            }
            while (token != null);

            try
            {
                foreach (CloudQueue cloudQ in cloudQueueList)
                {
                    //call this, or else your metadata won't be included for the queue. 
                    cloudQ.FetchAttributes(); 
                    Console.WriteLine("Cloud Queue name = {0}, class type = {1}", cloudQ.Name, cloudQ.Metadata["classtype"]);
                    queueNames.Add(cloudQ.Name);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception thrown listing queues: " + ex.Message);
                throw;
            }

            //clean up after ourselves and delete queues. 
            foreach (string oneQueueName in queueNames)
            {
                CloudQueue cloudQueue = cloudQueueClient.GetQueueReference(oneQueueName);
                cloudQueue.DeleteIfExists();
            }
            Console.ReadKey(); 
        }
    }
}

Однако может быть проще создать подкласс QueueMessage, затем удалить каждое сообщение из очереди и определить, какой подкласс вы в данный момент просматриваете, а затем передать его соответствующему обработчику.

person Rob Reagan    schedule 14.04.2017
comment
Спасибо, я понял, что это способ сделать это. Я сохраняю тип сообщения в метаданных, затем перечисляю очереди и использую отражение, чтобы найти реализацию IHandler для типа сообщения. Еще не проверял, но думаю будет работать. Кстати, спасибо за подсказку о FetchAttributes. - person Leon Cullens; 15.04.2017