Azure Functions - Service Bus Trigger

queue of people

With the scale and interaction of applications, event-driven architectures have proven why they are popular time and time again. Azure has several solutions for various event-driven architectures, the focus of this post is Azure Service Bus. So let's get into it.

First, we will need to provision an instance of Azure Service Bus to interact with. There are several blogs already on this topic, here is a walk-through from the Microsoft team. With the resource provisioned, we will need to create a queue to consume from. Again, this is a well-documented process here is the Microsoft documentation on how to create a queue.

With a resource and a queue, next, we need something to add messages to the queue. One potential is to add messages manually via the tooling in Azure Portal. But, we are technical people. Technical people require technical solutions. The way I approached this problem is to create a small console application to build and enqueue messages. Since the focus of this blog post is to discuss Azure Functions as a consumer of messages from Service Bus, I will not detail the event generator. The implementation can be found here. There are several comments in this project to assist in understanding how messages are enqueued.

Now that messages are on the queue, how can we consume them? As you have already read, this can be accomplished with an Azure Functions trigger. What a surprise! As you may have seen in the other posts on Azure Functions, the most everything is the same. The main difference is the annotation and type of the first argument. Here is a basic implementation of the queue consumer.

public static class GreeterConsumer
{
    [FunctionName("GreeterConsumer")]
    public static void Run(
        [ServiceBusTrigger("greeter", Connection = "ServiceBusConnection")] Person person, 
        ILogger log)
    {
        log.LogInformation($"Hello, {person.FirstName} {person.LastName}");
    }
}

Like every other Azure Function implementation, the method has to be decorated with the FunctionName annotation. The same rule applies to the uniqueness of the name of the function. As already eluded to above, the type of the first argument is Person this is a type that I defined in the models project so both the EventGenerator and Consumer projects can share the model. That first argument is decorated with a ServiceBusTrigger annotation. This is the mechanism to bind the function to a Service Bus. The annotation requires additional information to establish a connection to the Azure Service Bus. The first argument for the trigger annotation is the name of the queue the function is to be reacting to. The second is the connection to said Service Bus. Notice, the value of Connection. This value is a key in the local.settings.json file.

With all these pieces built and ready to execute. First, we should add are a couple of items to the queue. Then set the consumer project as the startup project. Start the project with the debugger attached and you will see log messages in the terminal window greeting all the people that appear in the queue.

So what just happened? When the EventGenerator project is run, the tool builds a mock list of People. The list is then serialized and added to an Azure message. The list is enqueued in the Azure Service Bus Queue that we provisioned. Since no consumer is listening to the queue the messages just sit and wait. Once the consumer project is started, a connection between the function and the queue is established. One by one, the messages on the queue are removed from the queue and processed by the consumer.

This is all there is to it. Not complicated at all. Of course, there is always room to add more sophisticated logic to production quality consumers. But, for the sake of example this gets all the pieces built and operational.