Messaging Made Easy With Azure Service Bus

Messaging Made Easy With Azure Service Bus

If you're working in a distributed system, you need to be able to communicate between multiple services. There are a few ways that you can implement this. Depending on your chosen approach, you can either introduce tight coupling between your services or stay loosely coupled.

Loose coupling is an important quality in distributed systems. It allows you to evolve your services independently. So how do you implement loosely coupled communication between services?

You need a messaging system.

And Azure Service Bus is an excellent choice.

In this week's newsletter, I'll show you how to create an Azure Service Bus instance, and how to implement messaging over a queue.

Let's dive in.

Creating An Azure Service Bus Instance

You can create a new Azure Service Bus instance from the Azure portal.

I won't go into detail on that, since I find the Azure UI pretty intuitive.

After creating your Azure Service Bus instance, you'll need to do two more things:

  • Create a new queue

  • Find the connection string

After that you can proceed with implementing the pub-sub pattern over a queue. And you'll use the connection string from the Azure portal for connecting to the Azure Service Bus instance.

Publishing Messages To The Azure Service Bus Queue

The first thing we need to do is to create the publishing side of our system, and then we'll see how we can process messages. We're going to use the Azure.Messaging.ServiceBus library to connect to the queue running in Azure Service Bus.

You can install the NuGet package by running the following command:

Install-Package Azure.Messaging.ServiceBus

And now, let's write the code for publishing a message to an Azure Service Bus queue.

To work with the Azure Service Bus instance, you will use the ServiceBusClient class. It requires a connection string to be able to connect to the Azure Service Bus instance.

The ServiceBusClient is safe to cache and reuse in the application, so it can be registered as a service with the singleton lifetime.

With the ServiceBusClient you can create a ServiceBusSender instance, which is responsible for sending the actual messages. You also need to specify which queue it will be sending messages to. This can also be the name of a topic, if you are publishing to a topic instead.

using Azure.Messaging.ServiceBus;

await using var client = new ServiceBusClient(ConnectionString);

await using ServiceBusSender sender = client.CreateSender(QueueName);

// This will be the payload for the message ✉️
var productCreated = new ProductCreatedEvent(
    eventId: Guid.NewGuid(),
    product.Id,
    product.Name);

string json = JsonSerializer.Serialize(productCreated);

var message = new ServiceBusMessage(json);

await sender.SendMessageAsync(message);

As you can see, publishing a message to the queue is relatively simple.

We're creating a new instance of ProductCreatedEvent, which represents our message payload. The payload itself is serialized into a JSON string, wrapped inside a ServiceBusMessage.

For publishing messages to Azure Service Bus, you simply call the SendMessageAsync method.

Receiving Messages From The Azure Service Bus Queue

Publishing messages to a queue is only half of the job. You also need to be able to receive messages from the queue. However, you'll see that this is very similar to the publishing side.

On the receiving side, you'll also need to create a ServiceBusClient. And then use it to create an instance of ServiceBusProcessor, which is used for consuming messages. You need to tell the ServiceBusProcessor which queue it will subscribe to.

The ServiceBusProcessor exposes two events, which represent callbacks for when a message is received. These events are ProcessMessageAsync and ProcessErrorAsync.

You need to provide a handler for these two events, to properly consume messages from the queue. In the example below, we're using the HandleMessageAsync and HandleErrorAsync local functions.

using Azure.Messaging.ServiceBus;

await using var client = new ServiceBusClient(ConnectionString);

await using ServiceBusProcessor processor  = client.CreateProcessor(QueueName);

processor.ProcessMessageAsync += HandleMessageAsync;

processor.ProcessErrorAsync += HandleErrorAsync;

await processor.StartProcessingAsync();

async Task HandleMessageAsync(ProcessMessageEventArgs args)
{
    string json = args.Message.Body.ToString();

    var productCreated = JsonSerializer.Deserialize<ProductCreatedEvent>(json);

    Console.WriteLine(productCreated);

    await args.CompleteMessageAsync(args.Message);
}

Task HandleErrorAsync(ProcessErrorEventArgs args)
{
    var exception = args.Exception;

    Console.WriteLine(exception.ToString());

    return Task.CompletedTask;
}

The ServiceBusProcessor begins listening to messages coming from the queue after calling the StartProcessingAsync method.

Notice that the success and error event handlers need to accept the ProcessMessageEventArgs and ProcessErrorEventArgs, respectively.

From the ProcessMessageEventArgs you can access the Message.Body which contains the mesage payload.

Further Reading

Azure Service Bus is a very feature rich service cloud. I showed you how to work with queues, which are great if you only have one publisher and one subscriber. However, if you need the ability to have multiple subscribers to a single message, you can't achieve this with queues.

You will have to use topics, and I invite you to research this topic further (pun intended).

Azure Functions have excellent support for integrating with Azure Service Bus. You can define a QueueTrigger that will run your Azure Function when you receive a message to an Azure Service Bus queue.

I also released a video showing how to publish and consume messages using RabbitMQ, and I think you might enjoy it after reading this newsletter.

Have an excellent weekend, and stay awesome!


P.S. Whenever you’re ready, there are 2 ways I can help you:

  1. Pragmatic Clean Architecture: This comprehensive course will teach you the system I use to ship production-ready applications using Clean Architecture. Learn how to apply the best practices of modern software architecture. Join 950+ students here.

  2. Patreon Community: Think like a senior software engineer with access to the source code I use in my YouTube videos and exclusive discounts for my courses. Join 820+ engineers here.