Creating a Simple Workflow with Azure Webjobs and Service Bus
With the announcement of an upgrade to the webjobs service in Microsoft Azure service bus triggers to Topics and Queues were added.
This got me thinking about how they could be used and there are a lot of scenarios where events need to trigger simple actions as well as running things off of a timer. Utilising Service Bus Topics along with a number of filtered subscriptions led me to create a simple workflow using Webjobs and Topics.
Setting up web jobs is fairly easy and the two links at the top of this article will give you a good start to this along with the 3 articles written by Mike Stall (about Blobs, Queues and Tables) (although the syntax has changed with the latest update, but its easy enough to work out the changes)
The first thing to note which I didn’t pick up on straight away was that you need to make sure that you set up connection strings for the diagnostics to work. I did this in the Configure section of the web site where I was deploying the webjob. I also added it to my app.config file so that I could debug locally in Visual Studio
They all point to my Azure Storage account.
The way I set this up was to create a message class that contained the data that I wanted to send between states. This message class would be wrapped in a BrokeredMessage and I would use the properties to determine the state of the message. The message is then added to a Service Bus Topic. By setting properties on the message, I could create a number of subscriptions that had an SqlFilter applied which would allow the subscription to only contain messages of a specific type.
When coding this I created a basic console application and added the following code to the Main method:
1// now create the subscriptions for the states if (!\_namespaceManager.SubscriptionExists(TopicName, TopicName + "start")) { \_namespaceManager.CreateSubscription(TopicName, TopicName + "start", new SqlFilter("State='WorkflowStart'")); } if (!\_namespaceManager.SubscriptionExists(TopicName, TopicName + "state1")) { \_namespaceManager.CreateSubscription(TopicName, TopicName + "state1", new SqlFilter("State='WorkflowState1'")); } if (!\_namespaceManager.SubscriptionExists(TopicName, TopicName + "state2")) { \_namespaceManager.CreateSubscription(TopicName, TopicName + "state2", new SqlFilter("State='WorkflowState2'")); }
This allows me to create Topic triggers for the webjob for specific states as follows:
1public static void SimpleWorkflowStart( \[ServiceBusTrigger(TopicName, TopicName + "start")\] BrokeredMessage message, TextWriter log) { log.WriteLine("Workflow Started"); . . . }
Two things to note with the method above. Firstly, the ServiceBusTrigger requires both the Topic name and the subscription name in order to receive the filtered list of messages. Secondly, by adding the TextWriter you can then send logging information to the dashboard, which is useful when trying to diagnose the webjobs when deployed to Azure.
I then setup a number of other methods for each subscription for each state. This allows me to perform whatever action I wanted to in each state. After carrying out the action I modified the message by changing the state property and then put it back onto the Topic. You can’t use the same brokered message so you either have to copy all the data out of the existing message and create a new one or you can use the Clone method on Brokered message (the easy option ).
1// copied the old message BrokeredMessage newMessage = message.Clone(); // change the state newMessage.Properties\["State"\] = "WorkflowState2"; TopicClient tc = TopicClient.CreateFromConnectionString( ConfigurationManager.ConnectionStrings\["ServiceBus"\].ConnectionString, TopicName); // delay sending the message a little newMessage.ScheduledEnqueueTimeUtc = DateTime.Now.AddSeconds(10);
In my example I also added a delay to the Enqueue of the message so that I could see things progressing in order. The enqueue time of the message is the delay before the message appears in the subscription. This could be also be used as a delayed trigger if you needed something done after a specific time interval. I also achieve this by adding the data to a Azure SQL db and then using a scheduled webjob to check the db for expired jobs. I created a separate console application for this which looked for messages that were expired due to a specific start date being older than a certain time, sending an email using SendGrid and then marking the record as complete in the db.
Webjobs made the actions easy to do and with the addition of the service bus triggers allowed me to have a fairly simple code structure to carry our a sequence of simple action (which is often what we need to do) without a heavy overhead of a workflow engine. It also utilised a hosting environment that I was already using.