Windows Azure is considered to be a cloud based operating system which allows developers to leverage a network of computers to permit what amounts to infinite scaling. This scaling is all managed within data centers run by Microsoft. Of the many uses for Azure one is computation. With the ability to leverage so many computers the prospect of performing heavy computation becomes very attractive. For this purpose, Azure supports what are known as Worker roles which act as background processing agents.
If you have flirted with Azure at all, you know that one of the most popular uses is to host web applications and allow for “n” instances to be spun up and down at will, this allowing companies to hug the demand curve rather then planning for the highest usage point. The roles which carry this out are called Web roles. However, the other type of role, the one which carries out background processing is called a Worker role.
Worker roles are much like Web roles in that they have OnStart and OnEnd events, but a web role is, by default, externally accessible, a worker role is not. Think of a worker role as a messenger boy, they allow for communication.
I have constructed a rather simple example to show them in action. What we have is a simple guestbook which allows a guest to leave his name, a message, and a picture. All of this information is stored in a blob and saved to my local storage account, however, the worker role is ticking every second and if its see’s a message in the queue, it will process that message. In this case, the message will be the address to the blob and the action will be to create three images: one resized to half the width of the original, one resized to 1/4 of the original, and the final one is the original itself with all meta information.
Below is the code for the Run method, which is invoked as the role begins executing, and the ProcessMessage method which executes a message when it is found in the queue:
1: public override void Run()
2: {
3: // This is a sample worker implementation. Replace with your logic.
4: Trace.WriteLine("ResizeWorker entry point called", "Information");
5:
6: // begin analyzing the queue
7: // we will check every 1 second for new queue items
8: do
9: {
10: var message = _helper.GetQueueMessage(Core.Constants.CLOUD_QUEUE_NAME);
11: if (message == null)
12: {
13: Thread.Sleep(1000); // do a sleep on the thread before checking again
14: continue;
15: }
16:
17: // process the message
18: ProcessMessage(message);
19:
20: // remove the message from the queue as it has been processed
21: _helper.DeleteMessageFromQueue(Core.Constants.CLOUD_QUEUE_NAME, message);
22: } while (true);
23: }
24:
25: ///
26: /// Process a Queue Message to resize images
27: ///
28: ///
29: private void ProcessMessage(CloudQueueMessage message)
30: {
31: // we are going to create a medium size images (half the width of the original)
32: // and a small (1/4 the width of the original)
33: var container = _helper.GetContainer(Core.Constants.CLOUD_CONTAINER_NAME);
34: var blob = container.GetBlobReference(message.AsString);
35:
36: var pictureStream = new MemoryStream(blob.DownloadByteArray());
37: var mediumImageBytes = GetMediumImageBytes(pictureStream);
38: var smallImageBytes = GetSmallImageBytes(pictureStream);
39:
40: // save to the cloud
41: SaveToCloud(blob.Uri.ToString(), smallImageBytes, mediumImageBytes);
42: }
Personally, I really hate how they have this setup. Basically defining a busy wait inside the role as it looks for something to do. I thinking a better process would be to find a way to wire the role up a queue within the web role and make this event driven. Seems like it would make more sense given the structure of .NET in general.
This code is fairly straightforward. If you wish to see more of the code and how I am doing the resizing, I invite you to download the source available at the bottom of this page in Visual Studio 2010 format.
Worker roles are a very interesting concept, though by no means new to the world. The fact that they are totally managed by the Azure cloud makes them easy to deal with. I also suspect that using the FabricController you can control how many worker roles are spun up or down at a time. That is likely to be next experiment. The final piece of code to talk about is the action which handles the saving of the Guestbook sign request, for it also speaks to the Queue so the worker role can be alerted it has a job to do.
1: [HttpPost]
2: public ActionResult ProcessSign(string name, string message, HttpPostedFileWrapper picture)
3: {
4: using (StorageHelper helper = new StorageHelper("DataConnectionString"))
5: {
6: var container = helper.GetContainer(Constants.CLOUD_CONTAINER_NAME);
7:
8: string extension = Path.GetExtension(picture.FileName);
9: string blobName = Guid.NewGuid() + extension;
10: var blob = container.GetBlobReference(blobName);
11:
12: GuestBookEntry entry = new GuestBookEntry()
13: {
14: Message = message,
15: Name = name,
16: Picture =
17: new BinaryReader(picture.InputStream)
18: .ReadBytes(picture.ContentLength),
19: PictureType = picture.ContentType
20: };
21:
22: entry.SaveToBlob(blob);
23:
24: var queue = helper.GetQueueStorage(Constants.CLOUD_QUEUE_NAME);
25: var cloudMessage = new CloudQueueMessage(blob.Uri.ToString());
26: queue.AddMessage(cloudMessage);
27: }
28: return Index();
29: }
The key code to look at here is lines 24-26, though admittingly you wont see much as I have it wrapped by my StorageHelper class, which is available via the download. The way I have it setup the worker role checks every second for a message. Still even with such a short delay as I cannot guarantee the new pictures will be in place, I have to use the original for display or do a resize here and send the original dimensions as part of the message.
That is a weakness of the Worker role, with respect to web development. We will not see the work done instantly for things like resizing or even program maintenance. One has to be very careful when using Worker roles.
http://cid-630ed6f198ebc3a4.skydrive.live.com/embedicon.aspx/Public/PictureUploadAndResize.zip