Making the most out of your WorkerRoles

When creating your first WorkerRole you probably have a particular task in mind. It could be to generate a report every night or perhaps to send a welcome mail to newly registered users. The problems begins when you have another task that needs a worker, or a third. What you don’t want to do is to create a new WorkerRole instance for each task. You’ll be wasting both time and money.

This post will show you how to make the most of a single WorkerRole instance. I’ll use StructureMap but you can just as well use any IoC container.

Why?

Right, so the scenario that we try to avoid is the one where you create a new WorkerRole instance for each specific task.

I generate thumbnailsI send e-mailsI generate reports

The thing is, if you only need to generate reports once every night you’ll end up with a WorkerRole that sleeps most of the time. The other two instances will probably have something to do every now and then, but not nearly enough to occupy an entire dedicated WorkerRole instance. The primary reason for avoiding this is of course the cost (since you need to pay for every instance), but also the amount of work when creating a new work task. You’ll basically have to create a new project every single time you need some a new type of worker.

What then?

What you want is something like this.

We can do anything

Instead of having three instances we have three workers on a single instance. This is both cost effective and we can easily add more workers as we need them.

How?

Well to start with I have an IWorker that looks like this.

It’s a simple interface that gives us two properties and one method to implement. Priority will be used when sorting the workers. HasWork will tell us if the current worker has any job that needs doing. Finally, DoWork is where the Worker will do his task.

So, take for example the worker that sends e-mails, let’s call him SendMailWorker.

As you can see we take two dependencies in our constructor, IMailQueue and IMailSender. IMailQueue is basically a wrapper for an Azure Queue where we put our e-mails that we want to send and IMailSender actually sends our mail for us. If we would like to have another type of worker we would simply inject other dependencies in the constructor.

If we take a look at the HasWork property we see that we only check if the Queue contains any messages, actually we do a PeekMessage() on the queue.

The other two workers will look quite similar so I’ll skip posting their implementation, I’m guessing that you understand my point by now.

Now, in the StartUp method for the WorkerRole I’ll bootstrap StructureMap.

WithDefaultConventions tells StructureMap to register all instances that follow the pattern IFoo with the implementation Foo. TheCallingAssembly will look for instances to register in the executing assembly. By specifying AssemblyContainingType<IMailQueue> I make sure that we register both the IMailSender and IMailQueue, both located in the same assembly. Finally, AddAllTypesOf<IWorker> will register any class implementing IWorker.

Now we are ready to add some code to our Run method.

So, the first thing that we do is to fetch all registered instances of IWorker and sort them in priority order. Then we enter our infinite loop and start to process them one by one.

Result

If we would like to add another worker, all we have to do is to have implement IWorker. Then our IoC container will take care of the rest. Also, by injecting any external dependencies in the constructor we can easily implement our worker to do any kind of job. So we have both simplified the task of adding a new Worker and we also made it “free”. Success!

This Post Has One Comment

Leave a Reply

Close Menu