Background Processing with Worker Roles and Queues

A worker role runs in the background to provide services or execute time related tasks like a service process.
In this exercise, you create a worker role to read work items posted to a queue by the web role front-end. To process the work item, the worker role extracts information about a guest book entry from the message and then retrieves the corresponding entity from table storage. It then fetches the associated image from blob storage and creates its thumbnail, which it also stores as a blob. Finally, to complete the processing, it updates the URL of the generated thumbnail blob in the guest book entry.

Task 1 – Creating a Worker Role to Process Images in the Background

In this task, you add a worker role project to the solution and update it so that it reads items posted by the front-end from the queue and processes them.
  1. If not already open, launch Visual Studio as administrator from Start | All Programs | Microsoft Visual Studio 2010 by right clicking the Microsoft Visual Studio 2010 shortcut and choosing Run as administrator.
  2. In the File menu, choose Open and then Project/Solution. In the Open Project dialog, browse to \Source\Ex2-UsingWorkerRolesAndQueues\Begin, select Begin.sln in the folder for the language of your preference (Visual C# or Visual Basic) and click Open. Alternatively, you may continue with the solution that you obtained after completing the previous exercise.
  3. In Solution Explorer, right-click the Roles node in the GuestBook project, point to Add and then select New Worker Role Project.
  4. In the Add New Role Project dialog, select the Worker Role category and choose the Worker Role template for the language of your choice (Visual C# or Visual Basic). Set the name of the worker role to GuestBook_WorkerRole and click Add.
    Figure 23Adding a worker role project to the solution (C#)

    Figure 24Adding a worker role project to the solution (Visual Basic)
  5. In the new worker role project, add a reference to the data model project. In Solution Explorer, right-click the GuestBook_WorkerRole project and select Add Reference, switch to the Projects tab, select GuestBook_Data and then click OK.
  6. Next, add a reference to the System.Drawing assembly, only this time, in the Add Reference dialog, switch to the .NET tab instead, select the System.Drawing component and then click OK.
  7. Now, open the WorkerRole.cs file (for Visual C# projects) or WorkerRole.vb file (for Visual Basic projects) of the GuestBook_WorkerRole project and insert the followings namespace declarations.(Code Snippet – Introduction to Windows Azure - Ex2 WorkerRole Namespaces – CS)
    C#
    1. using System.Drawing;
    2. using System.Drawing.Drawing2D;
    3. using System.Drawing.Imaging;
    4. using System.IO;
    5. using GuestBook_Data;
    (Code Snippet – Introduction to Windows Azure - Ex2 WorkerRole Namespaces – VB)
    Visual Basic
    Imports System.Drawing
    Imports System.Drawing.Drawing2D
    Imports System.Drawing.Imaging
    Imports System.IO
    Imports GuestBook_Data
  8. Add member fields to the WorkerRole class for the blob container and the queue, as shown below.(Code Snippet – Introduction to Windows Azure - Ex2 WorkerRole Fields – CS)
    C#
    public class WorkerRole : RoleEntryPoint
    {
    1.   private CloudQueue queue;
    2.   private CloudBlobContainer container;
      ...
    }
    (Code Snippet – Introduction to Windows Azure - Ex2 WorkerRole Fields – VB)
    Visual Basic
    Public Class WorkerRole
      Inherits RoleEntryPoint
    
    
    

      Private queue As CloudQueue
      Private container As CloudBlobContainer
      ...
    End Class
  9. Insert the following code into the body of the OnStart method immediately after the line that subscribes the RoleEnvironmentChanging event and before the call to the OnStart method in the base class.(Code Snippet – Introduction to Windows Azure - Ex2 WorkerRole OnStart – CS)
    C#
    public class WorkerRole : RoleEntryPoint
    {
      ...
      public override bool OnStart()
      {
        // Set the maximum number of concurrent connections 
        ServicePointManager.DefaultConnectionLimit = 12;
    
    
    

    1.     // read storage account configuration settings
    2.     CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
    3.     {
    4.       configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));
    5.     });
    6.     var storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
    7.     // initialize blob storage
    8.     CloudBlobClient blobStorage = storageAccount.CreateCloudBlobClient();
    9.     container = blobStorage.GetContainerReference("guestbookpics");
    10.     // initialize queue storage 
    11.     CloudQueueClient queueStorage = storageAccount.CreateCloudQueueClient();
    12.     queue = queueStorage.GetQueueReference("guestthumbs");
    13.     Trace.TraceInformation("Creating container and queue...");
    14.     bool storageInitialized = false;
    15.     while (!storageInitialized)
    16.     {
    17.       try
    18.       {
    19.         // create the blob container and allow public access
    20.         container.CreateIfNotExist();
    21.         var permissions = container.GetPermissions();
    22.         permissions.PublicAccess = BlobContainerPublicAccessType.Container;
    23.         container.SetPermissions(permissions);
    24.         // create the message queue(s)
    25.         queue.CreateIfNotExist();
    26.         storageInitialized = true;
    27.       }
    28.       catch (StorageClientException e)
    29.       {
    30.         if (e.ErrorCode == StorageErrorCode.TransportError)
    31.         {
    32.           Trace.TraceError("Storage services initialization failure. "
    33.             + "Check your storage account configuration settings. If running locally, "
    34.             + "ensure that the Development Storage service is running. Message: '{0}'", e.Message);
    35.           System.Threading.Thread.Sleep(5000);
    36.         }
    37.         else
    38.         {
    39.           throw;
    40.         }
    41.       }
    42.     }
        return base.OnStart();
      }
      ...
    }
    (Code Snippet – Introduction to Windows Azure - Ex2 WorkerRole OnStart – VB)
    Visual Basic
    Public Class WorkerRole
      Inherits RoleEntryPoint
      ...
      Public Overrides Function OnStart() As Boolean
        ' Set the maximum number of concurrent connections 
        ServicePointManager.DefaultConnectionLimit = 12
    
    
    

        ' read storage account configuration settings
        CloudStorageAccount.SetConfigurationSettingPublisher(Function(configName, configSetter) configSetter(RoleEnvironment.GetConfigurationSettingValue(configName)))
        Dim storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString")
        ' initialize blob storage
        Dim blobStorage = storageAccount.CreateCloudBlobClient()
        container = blobStorage.GetContainerReference("guestbookpics")
        ' initialize queue storage 
        Dim queueStorage = storageAccount.CreateCloudQueueClient()
        queue = queueStorage.GetQueueReference("guestthumbs")
        Trace.TraceInformation("Creating container and queue...")
        Dim storageInitialized = False
        Do While (Not storageInitialized)
          Try
            ' create the blob container and allow public access
            container.CreateIfNotExist()
            Dim permissions = container.GetPermissions()
            permissions.PublicAccess = BlobContainerPublicAccessType.Container
            container.SetPermissions(permissions)
            ' create the message queue(s)
            queue.CreateIfNotExist()
            storageInitialized = True
          Catch e As StorageClientException
            If (e.ErrorCode = StorageErrorCode.TransportError) Then
              Trace.TraceError("Storage services initialization failure. " _
                  & "Check your storage account configuration settings. If running locally, " _
                  & "ensure that the Development Storage service is running. Message: '{0}'", e.Message)
              System.Threading.Thread.Sleep(5000)
            Else
              Throw
            End If
          End Try
        Loop
        Return MyBase.OnStart()
      End Function
      ...
    End Class
  10. Replace the body of the Run method with the code shown below.(Code Snippet – Introduction to Windows Azure - Ex2 WorkerRole Run – CS)
    C#
    public class WorkerRole : RoleEntryPoint
    {
      ...
      public override void Run()
      {
    1.     Trace.TraceInformation("Listening for queue messages...");
    2.     while (true)
    3.     {
    4.       try
    5.       {
    6.         // retrieve a new message from the queue
    7.         CloudQueueMessage msg = queue.GetMessage();
    8.         if (msg != null)
    9.         {
    10.           // parse message retrieved from queue
    11.           var messageParts = msg.AsString.Split(new char[] { ',' });
    12.           var imageBlobUri = messageParts[0];
    13.           var partitionKey = messageParts[1];
    14.           var rowkey = messageParts[2];
    15.           Trace.TraceInformation("Processing image in blob '{0}'.", imageBlobUri);
    16.           string thumbnailBlobUri = System.Text.RegularExpressions.Regex.Replace(imageBlobUri, "([^\\.]+)(\\.[^\\.]+)?$""$1-thumb$2");
    17.           CloudBlob inputBlob = container.GetBlobReference(imageBlobUri);
    18.           CloudBlob outputBlob = container.GetBlobReference(thumbnailBlobUri);
    19.           using (BlobStream input = inputBlob.OpenRead())
    20.           using (BlobStream output = outputBlob.OpenWrite())
    21.           {
    22.             ProcessImage(input, output);
    23.             // commit the blob and set its properties
    24.             output.Commit();
    25.             outputBlob.Properties.ContentType = "image/jpeg";
    26.             outputBlob.SetProperties();
    27.             // update the entry in table storage to point to the thumbnail
    28.             GuestBookDataSource ds = new GuestBookDataSource();
    29.             ds.UpdateImageThumbnail(partitionKey, rowkey, thumbnailBlobUri);
    30.             // remove message from queue
    31.             queue.DeleteMessage(msg);
    32.             Trace.TraceInformation("Generated thumbnail in blob '{0}'.", thumbnailBlobUri);
    33.           }
    34.         }
    35.         else
    36.         {
    37.           System.Threading.Thread.Sleep(1000);
    38.         }
    39.       }
    40.       catch (StorageClientException e)
    41.       {
    42.         Trace.TraceError("Exception when processing queue item. Message: '{0}'", e.Message);
    43.         System.Threading.Thread.Sleep(5000);
    44.       }
    45.     }
      }
      ...
    }
    (Code Snippet – Introduction to Windows Azure - Ex2 WorkerRole Run – VB)
    Visual Basic
    Public Class WorkerRole
      Inherits RoleEntryPoint
      ...
      Public Overrides Sub Run()
    
    
    

        Trace.TraceInformation("Listening for queue messages...")
        Do
          Try
            ' retrieve a new message from the queue
            Dim msg As CloudQueueMessage = queue.GetMessage()
            If msg IsNot Nothing Then
              ' parse message retrieved from queue
              Dim messageParts = msg.AsString.Split(New Char() {","c})
              Dim imageBlobUri = messageParts(0)
              Dim partitionKey = messageParts(1)
              Dim rowKey = messageParts(2)
              Trace.TraceInformation("Processing image in blob '{0}'.", imageBlobUri)
              Dim thumbnailBlobUri As String = System.Text.RegularExpressions.Regex.Replace(imageBlobUri, "([^\\.]+)(\\.[^\\.]+)?$", "$1-thumb$2")
              ' download original image from blob storage
              Dim inputBlob As CloudBlockBlob = container.GetBlockBlobReference(imageBlobUri)
              Dim outputBlob As CloudBlockBlob = container.GetBlockBlobReference(thumbnailBlobUri)
              Using input As BlobStream = inputBlob.OpenRead()
                Using output As BlobStream = outputBlob.OpenWrite()
                  ProcessImage(input, output)
                  ' commit the blob and set its properties
                  output.Commit()
                  outputBlob.Properties.ContentType = "image/jpeg"
                  outputBlob.SetProperties()
                  ' update the entry in table storage to point to the thumbnail
                  Dim ds = New GuestBookDataSource()
                  ds.UpdateImageThumbnail(partitionKey, rowKey, thumbnailBlobUri)
                  ' remove message from queue
                  queue.DeleteMessage(msg)
                  Trace.TraceInformation("Generated thumbnail in blob '{0}'.", thumbnailBlobUri)
                End Using
              End Using
            Else
              System.Threading.Thread.Sleep(1000)
            End If
          Catch e As StorageClientException
            Trace.TraceError("Exception when processing queue item. Message: '{0}'", e.Message)
            System.Threading.Thread.Sleep(5000)
          End Try
        Loop
      End Sub
      ...
    End Class
  11. Finally, add the following method to the WorkerRole class to create thumbnails from a given image.(Code Snippet – Introduction to Windows Azure -Ex2 ProcessImage – CS)
    C#
    public class WorkerRole : RoleEntryPoint
    {
      ...
    1.   public void ProcessImage(Stream input, Stream output)
    2.   {
    3.     int width;
    4.     int height;
    5.     var originalImage = new Bitmap(input);
    6.     if (originalImage.Width > originalImage.Height)
    7.     {
    8.       width = 128;
    9.       height = 128 * originalImage.Height / originalImage.Width;
    10.     }
    11.     else
    12.     {
    13.       height = 128;
    14.       width = 128 * originalImage.Width / originalImage.Height;
    15.     }
    16.     var thumbnailImage = new Bitmap(width, height);
    17.     using (Graphics graphics = Graphics.FromImage(thumbnailImage))
    18.     {
    19.       graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
    20.       graphics.SmoothingMode = SmoothingMode.AntiAlias;
    21.       graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
    22.       graphics.DrawImage(originalImage, 00, width, height);
    23.     }
    24.     thumbnailImage.Save(output, ImageFormat.Jpeg);
    25.   }
    }
    (Code Snippet – Introduction to Windows Azure - Ex2 ProcessImage – VB)
    Visual Basic
    Public Class WorkerRole
      Inherits RoleEntryPoint
      ...
      Private Sub ProcessImage(ByVal input As Stream, ByVal output As Stream)
        Dim width As Integer
        Dim height As Integer
        Dim originalImage As New Bitmap(input)
        If originalImage.Width > originalImage.Height Then
          width = 128
          height = 128 * originalImage.Height / originalImage.Width
        Else
          height = 128
          width = 128 * originalImage.Width / originalImage.Height
        End If
        Dim thumbnailImage As New Bitmap(width, height)
        Using graphic = Graphics.FromImage(thumbnailImage)
          graphic.InterpolationMode = InterpolationMode.HighQualityBicubic
          graphic.SmoothingMode = SmoothingMode.AntiAlias
          graphic.PixelOffsetMode = PixelOffsetMode.HighQuality
          graphic.DrawImage(originalImage, 0, 0, width, height)
        End Using
        thumbnailImage.Save(output, ImageFormat.Jpeg)
      End Sub
    End Class
    Note:
    Even though the code shown above uses classes in the System.Drawing namespace for simplicity, you should be aware that the classes in this namespace were designed for use with Windows Forms. They are not supported for use within a Windows or ASP.NET service. You should conduct exhaustive testing if you intend to use these classes in your own Windows Azure applications.
  12. The worker role also uses Windows Azure storage services and you need to configure your storage account settings, just as you did in the case of the web role. To create the storage account setting, in Solution Explorer, expand the Roles node of the GuestBook project, double-click GuestBook_WorkerRole to open the properties for this role and select the Settings tab. Click Add Setting, type “DataConnectionString” in the Name column, change the Type to ConnectionString, and then click the button labeled with an ellipsis. In the Storage Account Connection String dialog, choose the option labeled Use the Windows Azure storage emulator and click OK. Press CTRL + S to save your changes.

Verification

You now launch the updated application in the Windows Azure compute emulator to verify that the worker role can retrieve queued work items and generate the corresponding thumbnails.
  1. Press F5 to launch the service in the local compute emulator.
  2. Switch to Internet Explorer to view the application. Provided you completed the verification section of the previous exercise successfully, you will see the guest book entry that you entered, including the uploaded image displayed in its original size. If you recall, during the last task of that exercise, you updated the web role code to post a work item to a queue for each new entry submitted. These messages remain in the queue even though the web role was subsequently recycled.
  3. Wait a few seconds until the worker role picks up the queued message and processes the image that you are viewing. Once that occurs, it generates a thumbnail for this image and updates the corresponding URL property for the entry in table storage. Eventually, because the page refreshes every few seconds, it will show the thumbnail image instead.
    Figure 25Home page showing the thumbnail generated by the worker role
  4. If you are using Visual Studio 2010, in Server Explorer, expand the Blobs node in the Windows Azure Storage node, and then double-click the guestbookpics container. Notice that it now contains an additional blob for the generated thumbnail image.
    Figure 26Blob container showing the blob for the generated thumbnail
  5. Add some more guest book entries. Notice that the images update after a few seconds once the worker role processes the thumbnails.
  6. Press SHIFT + F5 to stop the debugger and shut down the deployment in the compute emulator.
source:http://msdn.microsoft.com