Статьи

Все, что нужно знать об обмене сообщениями через службу шины Azure (часть 1)


Возможность обмена данными между несвязанными системами не является проблемой, которую легко решить.
Распределенная система — это не новая концепция, а горячая тема, популярность которой постоянно растет, а потребность в обмене данными постоянно растет. Поскольку информация продолжает оставаться ядром наших приложений, необходимость обмена информацией и способы ее распространения будут по-прежнему создавать проблемы.

Сервисная шина Microsoft Azure — это облачная служба, которая позволяет обмениваться данными между несвязанными системами. В этой статье мы узнаем, как использовать служебную шину Azure с брокерской системой обмена сообщениями для распределения данных между системами. Однако если вы вообще знакомы со службами Azure, которые обеспечивают поддержку распределенных систем, вы будете знать, что Service Bus — не единственная служба такого типа. Служба хранилища очередей Azure также предоставляет аналогичные функциональные возможности и обеспечивает возможность обмена данными между распределенными системами. Так какой сервис очереди вам подходит? Эти вопросы, а также следующие темы — все области, которые мы будем охватывать.

Что такое сервисный автобус

Проще говоря, Service Bus — это вторая платформа Azure для организации очередей сообщений, которая предоставляет возможности ретрансляции и обмена сообщениями. Это многофункциональный и продуманный сервис, который может предоставлять независимым системам независимый обмен информацией.

Служебная шина Azure является одной из многих служб «Платформа как услуга» (PaaS) и может быть такой же простой, как отдельная очередь или очень сложный рабочий поток сообщений с почти бесконечным числом взаимосвязанных очередей, тем и подписок.

Шина обслуживания против хранения очереди

Как я уже указывал, служебная шина — не единственная служба сообщений, предлагаемая Azure, и не первая. Но между служебной шиной и хранилищем очередей, первой службой очереди сообщений Azure, есть существенные различия. Мы собираемся глубоко погрузиться в функции Service Bus с брокерским предложением обмена сообщениями, но важно, чтобы вы знали, когда использовать какую службу.

Microsoft предоставила сравнительный и контрастный документ, чтобы помочь вам принять это решение. Однако из-за ограниченных причин использования службы хранилища очередей мы можем быстро подвести итог, когда хранилище очередей будет лучшим выбором. Если вам нужен наименее сложный подход и ваше приложение удовлетворяет следующим требованиям, хранилище очереди будет лучшим выбором:

  • Необходимо хранить более 80 ГБ данных в очереди
  • Время сообщения меньше 7 дней
  • Возможность отслеживать обработку сообщений в очереди *

Как видите, размер хранилища сообщений и время его существования являются двумя ключевыми отличиями службы очереди хранения Azure. Естественно, аспект хранения данных службы очереди хранения связан с базовой службой хранения. Таким образом, если один из этих пунктов не является критическим требованием, сервис Service Bus станет более многофункциональным и универсальным вариантом.

Однако в службе Service Bus имеется более одной возможности обмена сообщениями. Эта статья посвящена служебной шине с посредническими сообщениями. Но передача сообщений через посредников — не единственная возможность обмена сообщениями, предлагаемая служебной шиной. Ретрансляция — это еще один вариант, о котором вы узнаете, исследуя служебную шину Azure, поэтому давайте немного разберемся с ними.

Brokered vs. Relay Messaging

До сих пор мы упоминали только «посреднические» сообщения с Azure Service Bus. Но это не единственная возможность обмена сообщениями, предоставляемая служебной шиной. Вместо схемы очередей сообщений, на которую мы ссылались до сих пор, обмен сообщениями через ретранслятор предоставляет возможность «переадресовывать» сообщения от службы на подключенный получатель. Это требует, чтобы получатель, ожидающий сообщение, был онлайн и доступен. Сильной стороной обмена сообщениями через ретранслятор является возможность предоставления конечной точки службы без типичного сетевого брандмауэра и скачкообразного изменения конфигурации инфраструктуры, чтобы сделать его доступным для внешних клиентов.

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

Таким образом, хотя предоставление конечных точек службы является одной из сильных сторон ретрансляции сообщений, а очереди обеспечивают долговечность брокерских сообщений, очереди имеют более одного вида. Следовательно, нам нужно рассмотреть различные варианты очереди, прежде чем мы перейдем к реализации компонента Service Bus Brokered Messaging.

Очереди против Темы и Подписки

Это может сбить с толку тех, кто только начинает знакомиться с Service Bus Brokered Messaging, поэтому я хочу попытаться сделать это как можно более понятным. Прежде всего, не упускайте из виду тот факт, что в конце дня мы всегда говорим об очередях. Service Bus Queue предоставляет простейший вариант доставки сообщений. Сообщения в очереди организованы по принципу «первым пришел — первым обслужен» (FIFO), и ожидается, что каждое сообщение будет обрабатываться одним потребителем. Мне нравится визуализировать очереди как одну трубу, где сообщение подается в трубу и потребляется одним потребителем на другом конце.

queue_message_full

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

topic_message_full

Мы будем вдаваться в подробности вышеизложенного, но на высоком уровне к теме может быть добавлено одно сообщение, и для каждого удовлетворенного правила подписки в эту подписку будет добавлена ​​копия сообщения. В этом случае каждая подписка становится очередью, где потребители могут обрабатывать сообщения по подписке индивидуально.

Начало работы со строительными блоками

Конечный результат, к которому мы стремимся, — это создание сообщений, которые могут потреблять потребители. Но чтобы попасть туда, нужно начать со строительных блоков. Одним из основных строительных блоков, которые вы будете использовать для основной части ваших прямых и косвенных взаимодействий с Service Bus, является объект NamespaceManager .

NamespaceManager облегчает возможность управлять и создавать основные объекты , такие как очереди, тема и подписки через фабричные методы просто назвать несколько. Однако у NamespaceManager есть пара зависимостей, которые мы должны предоставить, поэтому давайте сначала обсудим их.

Те зависимости, которые нам нужно реализовать, следующие:

  • Пространство имен служебной шины (используется Uri служебной шины)
  • Поставщик токенов
  • Сервис Автобус Сервис Uri

Пространство имен служебной шины — это именно то, как оно звучит, и в конечном итоге определяет наше личное пространство имен в рамках нашей службы служебной шины. Эта первая зависимость должна быть создана на портале Azure в разделе «Шина обслуживания».

создание-пространства имен

Вам нужно будет указать пространство имен (запомните это), в котором будет известна ваша конечная точка обслуживания служебной шины, а также такую ​​информацию, как регион, в котором она будет размещена. Наконец, есть важное замечание Базовый уровень против стандартного уровня обмена сообщениями. Для понимания каждого из них вы можете ознакомиться с этой документацией Azure и повлиять на возможность размещения тем и подписок.

Создав пространство имен, мы можем сосредоточиться на двух последних зависимостях; Поставщик токенов и сервисный автобус Uri. Поставщики токенов предоставляют механизм аутентификации, который  NamespaceManagerбудет использоваться. Есть несколько готовых провайдеров, которые можно использовать, и мы рассмотрим некоторые из них более подробно, когда мы перейдем к безопасности. Сейчас мы собираемся использовать провайдер токенов подписи общего доступа и указать имя и ключ политики подписи по умолчанию (SAS) по умолчанию. Эти две части информации можно найти в разделе «Настройка» недавно созданного пространства имен служебной шины на портале Azure. С помощью имени и ключа политики мы можем создать Token Provider:

TokenProvider tokenProvider =
TokenProvider.CreateSharedAccessSignatureTokenProvider(AccountInfo.PolicyName,
AccountInfo.Key);

ПРИМЕЧАНИЕ . AccountInfoПросто введите правильное имя и ключ политики подписи общего доступа.

Uri служебной шины предоставляет NamespaceManagerконечную точку службы, с которой будут работать операции. Здесь будет использоваться наше ранее созданное пространство имен. Конечная точка службы, такая как, mynamespace.servicebus.windows.net может быть создана с использованием одного из статических методов из класса ServiceBusEnvironment и с указанием нашего пространства имен и протокола.

//”sb” defines the scheme of the service Uri
Uri serviceBusUri = ServiceBusEnvironment.CreateServiceUri("sb", "shopit", string.Empty);

С помощью инструментов Visual Studio Azure многие шаблоны проектов Azure Service Bus с помощью пакета NuGet конфигурации Azure Service Bus. Но понимание этих базовых зависимостей поможет облегчить вашу собственную реализацию при необходимости.

Создание менеджера пространства имен

Зависимости в руках, теперь мы можем генерировать наши  NamespaceManager:

NamespaceManager namespaceManger = new NamespaceManager(serviceBusUri, tokenProvider);

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

NamespaceManagerSettings settings = new NamespaceManagerSettings {OperationTimeout = new TimeSpan(0, 1, 0), TokenProvider = tokenProvider};
NamespaceManager namespaceManger = new NamespaceManager(serviceBusUri, settings);

Я обсуждал реализацию настраиваемой политики повторных попыток для отслеживания фактических повторений попыток в этой статье, в которой используется блок приложения Enterprise Transient Fault Fault . Мы рассмотрим политику Retry позже.

Асинхронный подход

Одной из лучших рекомендаций, на которые часто ссылаются, является использование асинхронного подхода к разработке для операций вашей служебной шины. Это легко сделать, воспользовавшись асинхронными методами, предоставленными вам для большинства ваших операций. Вы обнаружите, что библиотека предоставляет вам 2 различных асинхронных шаблона, из которых можно выбрать модель асинхронного программирования или асинхронный шаблон на основе задач . Мы поговорим об этом больше, когда перейдем к рекомендациям. Чтобы попытаться сохранить верность лучшему опыту, а также тому факту, что большинство демонстраций проводятся синхронно, здесь большинство примеров будет доставляться асинхронно (каламбур)!

Чтобы перехватывать и обрабатывать выбрасываемые исключения с помощью асинхронных операций, вы должны как минимум обернуть свои вызовы в Try / Catch. Однако, если не продемонстрировать обработку ошибок, мы исключим этот код, чтобы сосредоточиться на продемонстрированной операции.

Очереди служебной шины

С нашим NamespaceManager созданием мы заложили основу для основной части операций, которые мы собираемся рассмотреть в отношении обмена сообщениями с сервисной шиной. Я уже объяснил два варианта доставки сообщений, которые предлагает служебная шина, и мы собираемся начать с самого простого маршрута, рассматривая использование очередей служебной шины.

Хотя очереди не поддерживают фильтрацию сообщений, которая делает разделы и подписки настолько уникальными, они поддерживают, сеансы, обнаружение дублирования, отсрочку сообщений, недоставленные буквы и срок действия сообщений — это лишь некоторые из них. Несмотря на то, что все эти функции также поддерживаются темами и подписками; Я решил пересмотреть очереди и темы / подписки по отдельности.

Создание очередей

Создание очереди — это первый шаг к тому, чтобы мы могли хранить и получать сообщения. Имена очередей всегда создаются строчными буквами, независимо от того, какой регистр вы указываете для имени очереди.

QueueDescription queueDescription = await namespaceManager.CreateQueueAsync(queueName);

Чтобы продемонстрировать несколько других служебных методов, мы можем сначала попытаться проверить, существует ли очередь, и вернуть информацию об уже существующей очереди, если она есть:

QueueDescription newQueueDescription = null;
if (!await _namespaceManager.QueueExistsAsync(queueName))
{
    newQueueDescription = await _namespaceManager.CreateQueueAsync(queueName);
}

QueueDescription queueDescription = newQueueDescription ?? await _namespaceManager.GetQueueAsync(queueName);

CreateQueueAsyncoverride принимает QueueDescriptionобъект, который позволяет указывать значения за пределами значений по умолчанию, с помощью которых создается очередь. Я неохотно обсуждал множество различных свойств очереди на ранней стадии, но поскольку некоторые из них не могут быть обновлены после создания очереди, мы могли бы также обсудить их сейчас.

Свойства очереди

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

1. DefaultMessageTimeToLive — это интересный. Это диктует время существования сообщения в двух сценариях: 1) если для сообщения не установлено TimeToLiveзначение прямого свойства или 2) когда сообщения TimeToLiveбольше, чем DefaultMessageTimeToLiveсвойство очереди . Однако, если значение сообщения TimeToLiveниже, оно TimeToLiveбудет временем истечения срока действия сообщения.

2. requireDuplicationDetection — позволяет включить обнаружение дублирования сообщений. Это работает в сочетании с DuplicationDetectionHistoryTimeWindow. Это одно из тех значений, которые должны быть установлены при создании и не могут быть обновлены впоследствии.

3. DuplicateDetectionHistoryTimeWindow — вы можете указать период времени, в течение которого очередь будет сохранять идентификаторы сообщений, чтобы выполнить обнаружение дублирования сообщений. Время не может превышать максимальное время, в течение которого сообщение может находиться в очереди, которое составляет 7 дней.

Имейте в виду, что с этой функцией накладные расходы. Идентификатор сообщения, который должен быть сохранен в очереди, имеет служебную информацию в 64 байта. Таким образом, простым примером будет очередь, которая обрабатывает 1000 сообщений, что потребует 64 000 байтов служебной информации. Это может показаться не таким уж большим, но сократите цифры, если вы отправляете 30 сообщений в секунду и пытаетесь сохранить DuplicateDetectionHistoryTimeWindowих максимум в течение 7 дней. Накладные расходы будут ~ 1 ГБ.

4. EnableDeadLetteringOnMessageExpiration разрешить перемещение ваших сообщений в очередь мертвых рассылок при достижении очереди DefaultMessageTimeToLiveили сообщений TimeToLiveпервым и истечении срока действия сообщения.

5. LockDuration — когда мы перейдем к обработке сообщений, мы поговорим больше о PeekLock, но этот параметр задает, как долго сообщение блокируется для обработки, прежде чем оно будет возвращено в очередь.

6. MaxSizeInMegabytes — общий размер очереди. По умолчанию используется 1 ГБ. На это негативно влияют накладные расходы, создаваемые DuplicationDetectionHistoryTimeWindow.

7. AutoDeleteOnIdle — фактически это временной интервал, который обозначает, как долго очередь может оставаться в живых, если она простаивает, прежде чем она будет автоматически удалена. Минимальное время 5 минут.

Обновление очередей

Некоторые свойства очереди могут быть обновлены после ее создания, а другие — нет. Ниже мы можем увидеть, как мы можем использовать QueueDescriptionобъект для обновления определенных свойств существующей очереди:

QueueDescription newQueueDescription = new QueueDescription("TestOperationQueue")
{
    DefaultMessageTimeToLive = TimeSpan.FromDays(3),
    AutoDeleteOnIdle = TimeSpan.FromHours(1),
    MaxSizeInMegabytes = 2048
};

QueueDescription  queueDescription = await _namespaceManager.UpdateQueueAsync(newQueueDescription );

Здесь мы обновили свойства очереди, чтобы время жизни не превышало 3 дня, а время ожидания очереди в режиме ожидания составляет 1 час. и максимальный размер очереди до 2 ГБ.


Удаление очереди с помощью
AutoDeleteOnIdleдействительно удаляет даже сообщения в очереди. Чтение: очередь простоя запускается при последнем постановке в очередь или снятии очереди сообщений.

Удалить очереди

Удалить не выдает и исключение в том случае, если очередь не существует.

_namespaceManager.DeleteQueueAsync(queuePath);

We can set the AutoDeleteOnIdle queue property to auto delete itself after it has went idle and the set time period has expired.

Sending Messages

Now that our queue is in place, we can exercise sending messages to the queue.  In order for that to be possible we need to generate a facilitator for sending messages.  When discussing strictly Service Bus Queue’s as we are, there are only 2 options (QueueClient and MessageSender).

It is important to understand that the MessageSender is an abstraction for the QueueClient and should be utilized unless it is absolutely necessary to get to functionality specific to a QueueClient.  We will see the benefits of utilizing the MessageSender over a QueueClient when we get to the subject of Topics.

We can create a MessageSender from one of the factory methods of a MessagingFactory. However, the queue author is not always the same entity that sends messages to the queue. Therefore, our MessagingFactory also requires a proper service bus endpoint and token provider just as our NamespaceManager did.

Earlier, when we generated our NamespaceManager we could have created it with a connection string instead of manually generating the two dependencies Service Bus Uri and Token Provider.  Therefore, for demonstration purposes, we’ll look at generating a MessageFactory using a connection string that we can acquire from our Azure portal.  You can acquire your service bus namespace’s Connection String by clicking on “Connection Information” at the bottom of the Azure portal after you have selected the “Service Bus” service icon.

conenctionStrings

The connection string is parsed to supply the Service Bus Uri (endpoint) and Token Provider (shared access signature) for the MessageFactory when we utilize the FromConnectionString method.

MessagingFactory factory = MessagingFactory.CreateFromConnectionString(AccountInfo.ConnectionString);
MessageSender messageSender = factory.CreateMessageSender(QueueName);

With a MessageSender we are prepared to send messages

//Some object
SingingTelegram singingTelegram = new SingingTelegram
{
    RecipientFirstName = "Elvis",
    RecipientLastName = "Presley",
    SongName = "Won't you come back again?"
};

BrokeredMessage message = new BrokeredMessage(singingTelegram);

await messageSender.SendAsync(message);

But hold on!

What Kind of Message Are You Sending?

Before we can exercise the “distribute” in Distributed System and start flinging messages at our queue, we need to have an understanding of what constitutes a message.  In our case, its simply.  We’re sending a BrokeredMessage.

But there is allot to understand about a BrokeredMessage that can be broken down to characteristics and features.  Allot of the features will be discussed under specifics areas, but for now I wanted to stop and discuss some of the characteristics you need to be aware of when it comes to BrokeredMessages.

First off, a message at its heart is made up of a body and properties. The total max size of a message is 256kb.  The max size of all properties is 64kb.  The max size of the body is the remainder of the max size of the message and current size of the properties.  There are ways to increase the max size (i.e. sessions) but were not discussing that at the moment.

Due to certain protocol limitations such as HTTP header sizes, it is recommended that you keep your custom properties between 2 – 4 kb.

Body

The body is simply any serializable object and would constitute the payload of the message.  Whether you need to use the body of the message is up to you.  You will see that there are a number of different BrokeredMessage constructors.  These are generally all setting the body of the message as in:

BrokeredMessage msg1 = new BrokeredMessage("My Message Body");
BrokeredMessage msg2 = new BrokeredMessage(new SingingTelegram());

Properties

Properties are made up of two parts, system and custom properties.  The custom properties are simply a key/value pair collection that can be any string / object.  They allow us to transport specific custom properties at the header level of our message.  However, custom properties really come into play when we get to the discussion of subscription rules when we get to Topics and Subscriptions.

Another way to think about message properties in general is when discussing a message flowing between different protocols (HTTP/S, AMQP, SMB).  A message originating from HTTP/S request would end up mapping request headers to message properties.

When we want to promote data to the header level of a message we can utilize the custom Properties property as easily as:

BrokeredMessage message = new BrokeredMessage(singingTelegram);

foreach (object info in importantInformation)
{
    message.Properties.Add(info.Key, info.Value);
}

There are a considerable amount of important system properties on a BrokeredMessage that you can adjust.  Many of these we’ll touch on in specific features.  However, a few worth mentioning are:

1. ScheduledEnqueueTimeUtcIfyou wanted to delay the visibility of a message on a queue, you can set a future UTC time in which the message will be unavailable until the specified UTC time.

2. ExpiresAtUtc – If set, will be a specific UTC time in which the message will expire.

3. TimeToLive – Similar to ExpiresAtUtc, this is an amount of time that must pass after the message has been enqueued that the message will expire once.  Both this property and the ExpiresAtUtc directly affect when a message is expired, one at a set time, while the other after an amount of time.

Again, this is not an exhaustive list and it would benefit you to be aware of some of the other influential properties such as how MessageId is used for message duplication, or optional properties such as Label and ContentType can be used.

Receiving Messages

Namespace established.  Check.  Queue created.  Check.  Messages sent.  Check.  The obviously last piece of this simple scenario is the other aspect of a distributed system doing something with the messages we have enqueued.  As with a MessageSender we also want to use the MessageReceiver abstraction for getting messages.

To generate a MessageReceiver, we would need a MessagingFactory and you can refer back to Sending Messages for reference.

MessageReceiver messageReceiver = 
await _messagingFactory.CreateMessageReceiverAsync(queueName);

From here we can start to receive messages.  We can utilize the Timespan parameter of the ReceiveAsync to specify how long to poll before giving up.  In addition, the following example will do a continuous loop until the cancelation token is canceled.

while (!cancellationToken.IsCancellationRequested)
{
    //Wait up to 1 minute for a message
    var msg = await messageReceiver.ReceiveAsync(TimeSpan.FromMinutes(1)).ConfigureAwait(false);
    await ProcessAndReleaseMessageAsync(msg);
    await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken);
}

private async Task ProcessAndReleaseMessageAsync(BrokeredMessage message)
{
    MessageProcessingAction action = MessageProcessingAction.Abandon;
    try
    {
        //Process message
        action = MessageProcessingAction.Complete;
        await UpdateMessageState(message, action);
    }
    catch (Exception ex)
    {
        //log
    }
    finally
    {
        //C# 6.0 allows await calls in a finally blocks
        UpdateMessageState(message, action);
    }
}

private async Task UpdateMessageState(BrokeredMessage message, MessageProcessingAction action)
{
    switch (action)
    {
        case MessageProcessingAction.Complete:
            await message.CompleteAsync();
            break;
        case MessageProcessingAction.Abandon:
            await message.AbandonAsync();
            break;
        case MessageProcessingAction.Deadletter:
            await message.DeadLetterAsync();
            break;
        default:
            await message.AbandonAsync();
            break;
    }
}

Simply put, we wait up to 1 min for a message to be pulled from the queue.  Upon successfully pulling a message, the message is processed and then we explicitly notifying Azure Service Bus with the state of the message by calling CompleteAsync or AbandonAsync.  This is because of a default mode of the Queue where messages received are received in a PeekLock mode.

PeekLock specifies that a message is only temporarily checked out for some duration of time to be processed.  Unless the message is checked in as complete will it be removed from the queue.  There are other consequences that would directly affect a message being removed from the queue such as time to live setting of the queue or message.  But as far as message handling by our receiver, we will need to complete, abandon or dead-letter the message.  We’ll talk more about dead-letter queue and moving messages to there later.

As far as the example above, a couple of points; obviously, a switch statement could be more elegantly refactored, but would also obscure what I wanted to demonstrate about updating the message state so that the queue can properly handle it.  Whether that means leaving it on the queue in the case where our attempt to process it failed, or setting it to be complete so the queue removes it.

PeekLock is not the only mode that messages can be received.  Let’s look at message receive modes as well as other Queue behaviors and features I have withheld up until now.

Dealing with Queues, Again!

I have purposely tried not to over complicate queues with all the different features.  But before closing out Part 1 on Service bus, I did want to return to talk about a few important points that are worth noting.

As I already mentioned, there are 2 modes of message retrieval, PeekLock and ReceiveAndDelete (technically there is a 3rd Session + Peek, but not in the scope of this discussion).  We have already discussed PeekLock, so RecieveAndDelete is a less durable option as it removes the message from the queue when it is retrieved.  Therefore, as you might expect, any exception in your message processing that you have not accounted for can cause a loss of that message.  We can change this when creating our MessageReceiver:

MessageReceiver messageReceiver = 
await _messagingFactory.CreateMessageReceiverAsync(queueName, ReceiveMode.ReceiveAndDelete);

As mentioned under the queue properties that we can set the LockDuration of a message when received under the PeekLock mode which dictates how long a message can be retrained by a receiver before being released back on the queue to be processed.  This is at a default of 1 min., but can be set to a max 5 min.  In the case that a message’s LockUntilUtc property shows it is about to expire, we can request a renewal of the message lock:

await msg.RenewLockAsync();

Queues have two modes of delivery, Pull and Forward.  So far we have seen pull when our MessageReceiver pulls on request to the queue to receive a message.  But we can forward messages by setting the ForwardTo property of our queue so that any messages delivered will automatically be forwarded onto another queue.

QueueDescription queueDescription = await namespaceManager.CreateQueueAsync(new QueueDescription(QueueName)
{
    ForwardTo = destinationQueueName
});

Part 1 Conclusion

To recap, we learned allot of the differences that make up the Azure Service Bus, how to manage all aspects of Queues, as well as how to send and receive messages through Queues.  Now, we have already covered allot with the Service Bus, but there is allot more to cover.  Don’t worry though, I’ll let you take a breather and return in Part II to cover topics such as Topics and Subscriptions, Dead Lettering, Auto Forwarding, Transactions, Batch processing, Security and Service Bus best practices.