Author: Jeremy Holley
At a recent client, I was tasked with creating an Azure Queue Service bus. Setting up the queue itself is very easy using the Azure dashboard. Getting messages out of the queue is a bit more complicated. I decided to use the Worker Role for Azure Service Bus to accomplish the handling of messages. The advantage of using worker roles is the load leveling they provide and scalability with their ease of spinning up more instances of the worker in Azure.
Azure Worker Role
The Worker role provides the mid-tier business logic for a multi-tier queue service. It pops messages off of the Queue and handles the message based on type via a handler factory.
Set up the development environment
Before we can begin to develop the Worker Role we need to set up Visual Studio properly.
- Connect Visual Studio to an Azure account.
- You may need the Azure SDK installed for you to see the proper projects.
- In Visual Studio, create a new Project.
- Select Azure Cloud Service. In this example I am going to name mine ‘WorkerRoleExample’.
- In the next dialog select ‘Worker Role.’
Doing this we will be given a Solution with two projects
We will need to add two more projects to define messages and message handlers. You don’t necessarily need to have them in separate projects but I like to organize my Worker solutions in this manner.
The first project we will Name ExampleRole.Contracts and it will be a Class Library.
The Second Project we will name ExampleRole.Processors and it will also be a class library.
Once those are completed our Solution should look something like this.
We will now start coding the worker. Starting in the ExampleRole.Contracts Project
Example Role Contracts
Before we can start coding we need to make sure that we have a reference to JSON. We need to add the Newtonsoft NuGet package for it.
First, we will add a EventTypes enumeration. Here we will define some basic event types. First, we will have an announce message that we will use to announce that the queue worker is alive. This will be for internal logging/tracing and to show that a message has been handled on start up. The other initial type we will call NoEvent that one we will use for test handling messages. The enumeration should look like this initially.
Beyond these two message types we could define actual messages that we need to handle from the queue. What I like to do is to number my real messages starting at 100 and counting by 100’s from there to give room for message details. For example, if we have to do a create, update and delete processes on an Order object we could have something along the lines of the below example.
Next, we will define the base message format. We will add a new class MessageHeaderBase
Here we defined the header properties for the message. For the header items, we will use three properties.
Version number so that if the message changes we can handle older messages based on version number. This is not strictly required but it is a way to handle changes to the worker when you may have queue clients sending messages that are slightly different.
EventType refers to the enumeration EventTypes and tells the worker what handler to send the message to.
EventName is a friendly name for the event that can be used in logging to help humans see what sort of messages are being passed. This field is not absolutely necessary and is more of field to help troubleshooting.
Lastly, the message base will be created in a new class MessageBase. Here the class will inherit MessageHeaderBase and will have the EventData field which will carry the payload of the message and can be various types of data so we will use a generic type for it.
With that the class should look something like this.
Now we will move on to adding a class to handle the serialization of messages into and out of JSON.
In the From method we intake a generic model and serialize the object into JSON.
Then in the ToModel we take serialized JSON and try to convert it into one of our defined message contracts.
The class should end up looking something like this.
Finally, we will add a folder to hold our messages and then define our two basic messages.
We will define the AnnounceMessage Class. In this class we will have a faily simple string that just passes a string announce message to its handler. It should end up looking like this.
We have the event type set to announce and our friendly name is the same. Finally, its data is the string that is past in when the message is created.
Now we will define a test message that we can use to send just basic test data into the queue. This message contract is similar to the announce message but instead of passing a simple string we will pass a sub object that contains a Name property that is a string and a number property. This will show that complex object can be past as payloads.
We are done with the contracts project. Now we will move onto the ExampleRole.Processors project.
Example Role Processors
To start we need to add a reference to the ExampleRoles.Contracts project
We will start coding by creating an Interface for the message handlers called IMessageHandler.
The interface is very simple with a single method Handle. All it does is allows the factory to push out instances of the various handlers.
Now we will add handlers for our two message contracts.
We need to add the AnnounceHandler. This is a simple message so all we need to do is to take the message’s data and push it to the trace listener because it is used to see if the worker is alive.
For test messages, we will create a TestHandler. In this example, we will just push the message information to the trace log but here you could handle the message in various other ways. You could push the data to a SQL database or onto some other end point.
Now that we have handlers for our message contracts we can define handler factory. We will add a new class called MessageProcessor.
When instantiating the class we will need to add the handlers to a dictionary that defined what messages can be handled. Our starting code will look like this:
From there we will need to define the AddHandler method. In that method, we will add the handlers to the dictionary.
Finally, we will need to add another method to get the processors called GetProcessor.
Once we are done with that we are done with the Processor project.
Example Role Project
TO get started we will need a reference to both the Contracts and Processors projects to be added.
We will start coding in the WorkerRole.cs file.
There we will find a lot of code already in place for us but we are missing a class level variable for the QueueClient which is in the Microsoft.ServiceBus.Messaging name space. This might be missing from the project so we will need to add a reference to it using NuGet.
We are looking for the WindowsAzure.ServiceBus package
Once that is installed we can add the QueueClient to the code and the queue name also. Once added the code will look something like this. All that needs to be changed is the QueueName constant needs to be changed to the name of your queue in Azure.
Now we move onto the Run Method WorkerRole.cs. Here will do the basic tasks of the message pump.
In the run method, we setup the message options. We want the messages to autocomplete to callback the complete call once the message processing has concluded. We only on 1 concurrent callback per pump right now and finally if we get an exception we want to send that exception to the LogErrors Method.
Next, we want to initiate the client’s OnMessage routine. Here we receive a message from the Queue and get the text of the message body. We then send that body payload to the ProcessMessage method. If an exception occurs, we catch it and log it. Finally, we tell the pump to wait one tick before checking for another message on the queue.
Now we will define the ProcessMessage method.
In the process message method, we do a basic handler factory where we find the type of message we are dealing with and instantiate the appropriate handler to handle the message’s payload. First it gets message header to find the type of message that it is dealing with. From there is gets the handler for the message type then has the handler process the message
Now we will define the LogError method to log any errors we will encounter.
In this example we will just push the errors to the trace log but in a production worker you could push the errors to a database or other logging solution.
Now we will move onto the OnStart method. This method handles the startup of the worker. If creates the queue client and sends the initial announce message(s).
Finally, we will handle the OnStop method. This method takes care of the cleanup of objects that need to be explicitly cleaned up.
With that we are done coding the worker. This example just starts a worker against a queue that needs to be defined by both the name in the WorkerRole.cs code and in the App.config in the app settings.
With the proper connection string and queue name this code should be able to handle the two message types we have defined.
\Worker Roles for Azures are a useful tool in any scalable multi-tier solution. They allow you to decouple your UI applications from direct service calls through asynchronous calls to a queue. This enables the components of the application to be disconnected and allows you to update pieces without losing functionality. If the worker is taken off line for updates or in case of failure the messages are still stored on the queue until the worker is available again. Another scenario would be for the consuming worker to only come online at certain times of the day allowing offloading of processing to nonpeak times.
Author: Jeremy Holley