Section 1: Creating a Worker Role and Test Web Role

Overview

In this lab, you will build a Worker Role that returns flight status information. This Worker Role will accept jobs through a queue and return flight status information through e-mail. You will then create a Web Role to test the Worker Role.

Objectives

In this lab, you will:

  • Learn to create a Worker Role and deploy it to Azure.
  • Learn to communicate with the Worker Role through a Web Role and queue.

System Requirements

You must have the following items to complete this lab:

Setup

This lab uses a Windows Live account to send flight status information via e-mail. If you do not have a Windows Live account, then proceed to https://signup.live.com.

This lab uses the Bing API to retrieve flight status information. In order to retrieve the flight information, you will need to have a developer application ID. If you do not have a developer application ID, proceed to https://www.bing.com/developers/createapp.aspx and sign up.

The Windows Azure SDK (included in Windows Azure Tools for Visual Studio) installs a simulation environment on your development machine for testing Azure applications locally before deploying them to the cloud. The simulation environment consists of the development fabric to host web and worker roles, and the development storage which simulates cloud blob, table and queue storage locally.

Development storage uses SQL Server as its underlying storage mechanism, and by default the SDK will attempt to configure it to use SQL Server Express. If you do not have SQL Server Express installed before installing the SDK, or you wish to simply use an existing SQL Server instance to host the development storage database, you must run the dsinit command to select the SQL Server instance where the database will be created.

Please see instructions below for how to run dsinit.

Using dsinit to Configure Development Storage

  1. Open a command prompt.
  2. Edit the following command line as appropriate for your environment, where [AzureSDKInstallDrive] is the drive where you installed the Azure SDK (or Windows Azure Tools for Visual Studio), and [YourSqlInstance] is the SqlServer where you want to create the development storage database.[AzureSDKInstallDrive]\ Program Files\Windows Azure SDK\v1.3\bin\devstore\dsinit.exe /sqlinstance:[YourSqlInstance]Example Command Line:“C:\Program Files\Windows Azure SDK\v1.3\bin\devstore\dsinit.exe” /sqlinstance:.
  3. Note that the sample command line above uses the value “.” for the sqlinstance argument, which specifies that the local default SQL instance will be used for development storage.

Exercises

This Hands-On Lab is comprised of the following exercises:

  1. Creating a Worker Role
  2. Creating a Web Role

Estimated Time to complete this lab: 45 minutes

Exercise 1: Creating a Worker Role

In this exercise, you will create a Worker Role that e-mails flight information.

Task 1 – Creating The New Worker Role Project

In this task, you will create the new Worker Role project.

  1. Start Visual Studio 2010.
  2. From the main menu, select File>>New>>Project.
  3. In the New Project dialog, select Cloud>>Windows Azure Project.
  4. Name the new project FlightInfoWorkerProject.
  5. Click OK.
  6. In the New Windows Azure Project dialog, select Worker Role and click the right arrow.
  7. Rename WorkerRole1 as FlightInfoWorkerRole.
  8. Click OK.

Task 2 – Defining Configuration Values

In this task, you will define the configuration values for the Worker Role. The configuration values establish connections to the storage account where the work queue will be created.

  1. Under the FlightInfoWorkerProject>>Roles folder, double-click FlightInfoWorkerRole.
  2. Click the Settings tab.
  3. Click Add Setting.
  4. Name the setting DataConnectionString.
  5. Set the Type to Connection String.
  6. Click the ellipsis under Value.
  7. In the Storage Account Connection String dialog, select Use the Windows Azure Storage Emulator and click OK.
  8. Click Add Setting.
  9. Name the setting SmtpServer.
  10. Set the Type to String.
  11. Set the Value to smtp.live.com.
  12. Click Add Setting.
  13. Name the setting LiveID.
  14. Set the Type to String.
  15. Set the Value to your LiveID.
  16. Click Add Setting.
  17. Name the setting LivePassword.
  18. Set the Type to String.
  19. Set the Value to your Live password.
  20. Click Add Setting.
  21. Name the setting BingAppID.
  22. Set the Type to String.
  23. Set the Value to your Bing Application ID.

    Figure 1

    Worker Role Settings

Task 3 – Coding The Worker Role

In this task, you will implement the Worker Role. Communication with the Worker Role will be through a queue.

  1. In the FlightInfoWorkerRole project, right-click the References node and select Add Reference.
  2. In the Add Reference dialog, click the .NET tab.
  3. Select System.Web.
  4. Click OK.
  5. In the Solution Explorer, open WorkerRole.cs for editing.
  6. Add the following statements to the top of the file.

    C#

    using System.Xml; using System.Xml.Linq; using System.Net.Mail; using System.Text; using System.IO; using System.Web.UI;

  7. Add the following member variables within the WorkerRole class.

    C#

    private CloudQueueClient qc; private CloudQueue q;

  8. Replace the entire body of the OnStart method with the following code.

    C#

    // Set the maximum number of concurrent connections ServicePointManager.DefaultConnectionLimit = 12; CloudStorageAccount.SetConfigurationSettingPublisher(( configName, configSetting) => { var connectionString = RoleEnvironment.GetConfigurationSettingValue(configName); configSetting(connectionString); }); return base.OnStart();

  9. Add the following code to the class to create a function that returns flight information from Bing.

    C#

    private XDocument GetFlightDetails(string AppID, string Flight, string Market) { string uri = String.Format( "https://api.bing.net/xml.aspx?AppId={0}&Query={1}& Sources=InstantAnswer&Version=2.2&Market={2}", AppID, Flight, Market); HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); StreamReader sr = new StreamReader(response.GetResponseStream()); XDocument flightResponse = XDocument.Load(sr); return flightResponse; }

  10. .Add the following code to the class to create helper functions that make build an HTML e-mail message with flight information.

    C#

    private string CreateHtmlMessage( string Subject, string Link, string AirlineName, string Status, string ScheduledDeparture, string UpdatedDeparture, string ScheduledArrival, string UpdatedArrival, string Origin, string Destination) { //Create HTML e-mail body StringWriter sw = new StringWriter(); using (HtmlTextWriter writer = new HtmlTextWriter(sw)) { //div writer.RenderBeginTag(HtmlTextWriterTag.Div); //anchor writer.AddAttribute(HtmlTextWriterAttribute.Href, Link); writer.RenderBeginTag(HtmlTextWriterTag.A); writer.Write(Subject); writer.RenderEndTag(); //table writer.RenderBeginTag(HtmlTextWriterTag.Table); //rows writer.Write(CreateTableRow("Airline Name", AirlineName)); writer.Write(CreateTableRow("Status",Status)); writer.Write(CreateTableRow("Scheduled Departure",ScheduledDeparture)); writer.Write(CreateTableRow("Updated Departure",UpdatedDeparture)); writer.Write(CreateTableRow("Scheduled Arrival",ScheduledArrival)); writer.Write(CreateTableRow("Updated Arrival",UpdatedArrival)); writer.Write(CreateTableRow("Origin", Origin)); writer.Write(CreateTableRow("Destination",Destination)); writer.RenderEndTag(); writer.RenderEndTag(); } return sw.ToString(); } private string CreateTableRow(string FieldName, string FieldValue) { StringWriter sw = new StringWriter(); using (HtmlTextWriter writer = new HtmlTextWriter(sw)) { writer.RenderBeginTag(HtmlTextWriterTag.Tr); writer.RenderBeginTag(HtmlTextWriterTag.Td); writer.Write(FieldName); writer.RenderEndTag(); writer.RenderBeginTag(HtmlTextWriterTag.Td); writer.Write(FieldValue); writer.RenderEndTag(); writer.RenderEndTag(); } return sw.ToString(); }

  11. .Add the following code to send the flight nformation as an e-mail.

    C#

    private void SendMail(string Server, string Subject, string Body, string LiveID, string Password, string ReturnAddress) { MailMessage mm = new MailMessage(LiveID, ReturnAddress); mm.IsBodyHtml = true; mm.Body = Body; mm.Subject = Subject; SmtpClient c = new SmtpClient(Server, 25); c.DeliveryMethod = SmtpDeliveryMethod.Network; c.EnableSsl = true; c.Credentials = new NetworkCredential(LiveID, Password); c.Send(mm); }

  12. Replace the entire body of the Run method with the following code.

    C#

    Trace.WriteLine("Worker entry point called", "Information"); //Reference Queue CloudStorageAccount sa = CloudStorageAccount.FromConfigurationSetting("DataConnectionString"); qc = sa.CreateCloudQueueClient(); q = qc.GetQueueReference("flightrequests"); q.CreateIfNotExist(); //Get Credentials string server = RoleEnvironment.GetConfigurationSettingValue("SmtpServer"); string liveId = RoleEnvironment.GetConfigurationSettingValue("LiveID"); string password = RoleEnvironment.GetConfigurationSettingValue("LivePassword"); string appID = RoleEnvironment.GetConfigurationSettingValue("BingAppID"); while (true) { Thread.Sleep(5000); Trace.WriteLine("Getting Message", "Information"); //Retrieve work request message CloudQueueMessage m = q.GetMessage(new TimeSpan(0, 5, 0)); XDocument doc = null; try { doc = XDocument.Parse(m.AsString); } catch { m = null; } if (m != null) { //Get flight information var flightRequest = (from e in doc.Descendants("Message") select new { Flight = e.Element("Flight").Value, Market = e.Element("Market").Value, ReturnAddress = e.Element("Email").Value }).First(); XDocument flightResponse = GetFlightDetails(appID, flightRequest.Flight, flightRequest.Market); XNamespace ia = "https://schemas.microsoft.com/LiveSearch/2008/04/XML/instantanswer"; XNamespace fls = "https://schemas.microsoft.com/LiveSearch/2008/04/XML/element/flightstatus"; var flightLink = (from d in flightResponse.Descendants(ia + "InstantAnswerResult") select new { Subject = d.Element(ia + "Title").Value, Link = d.Element(ia + "ClickThroughUrl").Value }).First(); var flightStatus = (from d in flightResponse.Descendants(fls + "FlightStatus") select new { AirlineName = d.Element(fls + "AirlineName").Value, FlightName = d.Element(fls + "FlightName").Value, Status = d.Element(fls + "StatusString").Value, ScheduledDeparture = d.Element(fls + "ScheduledDeparture").Value, UpdatedDeparture = d.Element(fls + "UpdatedDeparture").Value, ScheduledArrival = d.Element(fls + "ScheduledArrival").Value, UpdatedArrival = d.Element(fls + "UpdatedArrival").Value, OriginName = d.Element(fls + "OriginAirport").Element(fls + "Name").Value, DestinationName = d.Element(fls + "DestinationAirport").Element(fls + "Name").Value, OriginCode = d.Element(fls + "OriginAirport").Element(fls + "Code").Value, DestinationCode = d.Element(fls + "DestinationAirport").Element(fls + "Code").Value }).First(); //Create HTML document with flight info string htmlBody = CreateHtmlMessage( flightLink.Subject, flightLink.Link, flightStatus.AirlineName, flightStatus.Status, flightStatus.ScheduledDeparture, flightStatus.UpdatedDeparture, flightStatus.ScheduledArrival, flightStatus.UpdatedArrival, String.Format("{0}({1})", flightStatus.OriginName, flightStatus.OriginCode), String.Format("{0}({1})", flightStatus.DestinationName, flightStatus.DestinationCode)); //Email document SendMail(server,flightLink.Subject, htmlBody,liveId,password, flightRequest.ReturnAddress); //Delete work request message q.DeleteMessage(m); } }

Exercise 2: Creating a Test Web Role

In this exercise, you will create a simple Web Role to test the Worker Role locally before deploying to Azure.

Task 1 – Creating The New Web Role

In this task, you will add the new Web Role to the existing application.

  1. In the FlightInfoWorkerProject, right click the Roles folder and select Add>>New Web Role Project.
  2. In the Add New Role Project dialog, name the new Web Role FlightInfoWebRole and click Add.
  3. Under the FlightInfoWorkerProject>>Roles folder, double-click FlightInfoWebRole.
  4. Click the Settings tab.
  5. Click Add Setting.
  6. Name the setting DataConnectionString.
  7. Set the Type to Connection String.
  8. Click the ellipsis under Value.
  9. In the Storage Account Connection String dialog, select Use the Windows Azure Storage Emulator and click OK.

Task 2 – Initializing the Storage Connection

In this task, you initialize the storage connection to gain access to queue storage.

  1. Open Global.asax.cs for editing.
  2. Add the following references at the top of the file

    C#

    using Microsoft.WindowsAzure; using Microsoft.WindowsAzure.Diagnostics; using Microsoft.WindowsAzure.ServiceRuntime; using Microsoft.WindowsAzure.StorageClient;

  3. Add the following code to the Application_Start method.

    C#

    CloudStorageAccount.SetConfigurationSettingPublisher( (configName, configSetting) => { var connectionString = RoleEnvironment.GetConfigurationSettingValue(configName); configSetting(connectionString); });

Task 3 – Implementing The Test Web Role

In this task, you will implement the test Web Role. The Web Role will drop a flight request into the queue for the Worker Role to process.

  1. Open Default.aspx in Source view for editing for editing.
  2. Replace the ASPX markup in the BodyContent placeholder with the following markup..

    ASPX

    <div> <asp:Table ID="LayoutTable" runat="server"> <asp:TableRow> <asp:TableCell>Flight Number</asp:TableCell><asp:TableCell> <asp:TextBox ID="Flight" runat="server" /></asp:TableCell> </asp:TableRow> <asp:TableRow> <asp:TableCell>E-mail Address</asp:TableCell><asp:TableCell> <asp:TextBox ID="ReturnAddress" runat="server" /></asp:TableCell> </asp:TableRow> <asp:TableRow> <asp:TableCell>Market</asp:TableCell><asp:TableCell> <asp:TextBox ID="Market" runat="server" Text="en-us"/> </asp:TableCell> </asp:TableRow> </asp:Table> <br /> <asp:LinkButton ID="FlightRequest" runat="server" OnClick="FlightRequest_Click">Request Flight Status </asp:LinkButton> </div>

  3. Open Default.aspx.cs for editing.
  4. Add the following references at the top of the code file.

    C#

    using Microsoft.WindowsAzure; using Microsoft.WindowsAzure.Diagnostics; using Microsoft.WindowsAzure.ServiceRuntime; using Microsoft.WindowsAzure.StorageClient; using System.Xml.Linq;

  5. Add the following member variables to the class.

    C#

    private CloudQueueClient qc; private CloudQueue q;

  6. Add the following code to the Page_Load method.

    C#

    CloudStorageAccount sa = CloudStorageAccount.FromConfigurationSetting("DataConnectionString"); qc = sa.CreateCloudQueueClient(); q = qc.GetQueueReference("flightrequests"); q.CreateIfNotExist();

  7. Add the following code to the FlightRequest_Click event.

    C#

    XDocument data = new XDocument( new XElement("Message", new XElement("Flight", Flight.Text), new XElement("Market", Market.Text), new XElement("Email", ReturnAddress.Text))); CloudQueueMessage m = new CloudQueueMessage(data.ToString(SaveOptions.DisableFormatting)); q.AddMessage(m);

Task 4 – Building and Testing

In this task, you will test the solution locally.

  1. Right click the FlightInfoWorkerProject and select Build Solution.
  2. Press F5 to start debugging.
  3. When the Web Role appears, enter some flight information.

    Figure 2

    Flight Status Request

  4. Verify that you receive an e-mail from the Worker Role

    Figure 3

    Flight Status E-mail

Summary

Worker Roles are used in Azure for handling long-running or resource-intensive processes. Once deployed, you can communicate with Worker Roles either directly using endpoints (TCP, HTTP, HTTPS) or indirectly using queues. This lab communicated with a Worker Role using a queue.