Extensible Windows Azure projects using MEF

Here is how to enable a rich extensibility model for Windows Azure projects and how to run create jobs on Windows Azure Storage only once in your Windows Azure Projects. This sample and related AzureContrib release leverages Managed Extensibility Framework (MEF) – an upcoming .NET Framework component in .NET Framework 4.0.

We have made a small project at Dotway where I work on making the basic Windows Azure project template a bit more rich and intelligent. This has lead to three releases in the AzureContrib project, each one building on the last to make the functionality one more step richer.

The new release to AzureContrib adds a couple of important services (AzureContrib.ServiceHosting.ServiceRuntime.Services); the IWorkService And the IOneTimeWorkService. Also it adds a bit more intelligence to a Windows Azure Page, UserControl and most importantly to the Windows Azure WorkerRole.

Work Services

We have been using (MEF) throughout the AzureContrib project. Up until now it's mostly been used for Dependency Injection (DI) purposes. MEF in itself is not built to be a DI Container but solves the extensibility challenge it is built for by way of using this technique. If DI was the only thing we wanted to add to the Windows Azure Project template we could have used any of the many DI containers out there. In this release of Azure Contrib, however, the use of MEF and the rationale behind it becomes more apparent.

The current worker role in Windows Azure works by way of entering a perpetual loop, waiting for more work and executing it as it comes in. This is the canonical example for extensibility!

public override void Start()
{
    while (true)
    {
        // do work here 
    }
}

The problem arises when you want to begin adding more than one task to your loop. First of all the code in this loop in the main app will have to references all other code in your project. Second the maintenance of this little hamster wheel soon becomes a nightmare.

public override void Start()
{
    while (true)
    {
        // do work 1 
        // or work 2 
        // or work 3 
        // [...] 
    }
}

This is not very pretty or useful.

Enter the WorkService! ;~)

Work Service

The work service turns the above code mess into pure elegance:

protected override void OnStart()
{
    while (true)
    {
        var work = WorkService.GetWork();
        RoleStatus = work.DoWork();
    }
}

But wait a minute that's just hiding the problem away somewhere else! And what if there is no work?

To answer the question first; there is always work. If there actually isn't the work is an "idle" work task that puts the worker to sleep for a short while.

And, no, this WorkService does not hide the problem away somewhere else. The WorkService uses *MEF* to import all the available work, organize it and return the proper work task upon each request!

Inside the work service the main .GetWork() method looks something like this (comments below):

[Import]
public IWorkSchedulerProvider WorkSchedulerProvider { get; set; }

[Import]
public IRoleManager RoleManager { get; set; }

[ImportMany]
public IEnumerable<Lazy<IWork, IWorkMetadata>> Works { get; set; }

public IWork GetWork()
{
    var workScheduler = WorkSchedulerProvider.GetCurrentWorkScheduler();
    var orderedWorks = workScheduler.ScheduleWork(Works);

    foreach (var lazyWork in orderedWorks)
    {
        if (lazyWork.Value.HasWork)
        {
            return new WorkTemplate(lazyWork, RoleManager);
        }
    }
    return new IdleWork(RoleManager);
}

Comments:

  • [Import] IWorkSchedulerProvider – This is a Dependency Injected provider of a work scheduler used to sort the work in a good order. (See below)
  • [Import] IRoleManager – This gets us the AzureContrib wrapped/testable Microsoft.ServiceHosting.ServiceRuntime.RoleManager which is just used for logging.
  • [ImportMany] IEnumerable<Lazy<IWork, IWorkMetadata>> - Now stuff is getting exotic. In the project there may be any number of implementations of IWork that all have to be decorated by the metadata defined in IWorkMetadata. This is a standard MEF feature for importing a set of something that comes lazily with metadata. This means we can sort our available work according to metadata (which just happens to have an int Order property) and then lazy-invoke just one piece of work as requested.
  • .GetWork()  - When the work is sorted the instances are called one at a time to find the most prioritized work task that actually contains any work. The response is wrapped in a nice template that does logging for the work task name before as well as after the work is executed for easy log debugging. Each work task is self contained and can use Azure Storage at will.

Which brings us to the manipulation of Azure Storage items such as creation of message queues, entity tables and blob containers.

One Time Work Service

There’s a second work service provided in this release of Azure Contrib. The One-Time Work Service. What this service does is act as a MEF Shared instance (singleton) stateful service which will only execute it’s imported IOneTimeWork tasks once (per instance of a Windows Azure Web- or WorkerRole). This still means the work tasks have to be able to run more than once but it also means they will run ONLY once in the lifetime of a –Role instance. A step in the right direction.

How to use this

First you download the release (doh): Extensible Windows Azure Download.

The framework ships with updated base classes for Page, UserControl and WorkerRole which makes it über easy to use. Just inherit and you’re good to go.

The only challenge is to implement IWork and IOneTimeWork tasks since they have to 1) Be implemented, 2) Have the [Export(typeof(IWork)] or [Export(typeof(IOneTimeWork)] attribute and 3) provide the correct metadata as described by IWorkMetadata and IOneTimeWorkMetadata respectively.

Sample

The release also comes with a small sample that consists of a page that can add (fake) work to two different work queues and a worker role that picks up work tasks to run. The tasks are all sleep tasks. The interesting thing to check out in the sample apart from the code is the usage of logging that shows the roles staring up and logging IOneTimeWork and also shows each IWork task created and executed. The sample has two work tasks “A” and “B” where the first one is more prioritized in the worker.

Hope this enhanced Windows Azure Project Template can help you create Windows Azure Applications that are more testable, persistence ignorant and more extensible. This should make them very much more maintainable.

HTH!

Cheers,

M.

This code came about in cooperation with my good colleague Peter von Lochow.

Digg This

posted @ Monday, September 07, 2009 12:08 AM

Print

Comments on this entry:

No comments posted yet.

Your comment:



 (will not be displayed)


 
 
 
Please add 1 and 6 and type the answer here:
 

Live Comment Preview: