Windows Azure Web Role and Worker Role

Windows Azure WebRole and WorkerRole are different type of cloud services. Before continuing It is important define what is the Cloud Services and how they work. It is like a box that contain your application and its configuration. A role instance is a virtual machine where the application code and the configuration run. Suppose to work with Azure Web Sites or with a multi-tier application and you want to call processes to handle some tasks, with WebRole and WorkerRole you can perform these actions.

What are the differences ? These two services are very similar but WebRole provide a dedicated IIS web-server for hosting a web application and WorkerRole is for any other application that could be asynchronous and long-running/loop and independently from user action or input.

How can I use ? Both classes extend the abstract class RoleEntryPoint in the name space Microsoft.WindowsAzure.ServiceRuntime.
This class have three methods that you can override :
    - OnStart
    - OnStop
    - Run

The first method execute is the method OnStart. Typically there we put all the code to initialise our resources. When it is executing the status role is set as Busy. If the method OnStart is executed without error the second method that is executed is Run. In this method we could find an infinite loop that perform our action. If we return and terminate this method the system recognise that the app is stopped but it isn’t executed again, so to execute more time this routine we need to put a loop.

If the Run method return a value or if the shutdown is requested the last method that is called is the method OnStop. This ensure that any cleanup or shutdown process are completed. There is an hard limit of 5 minutes and then Windows Azure puts on shutdown all user processes.

Build an example project


To build a new WebRole and WorkerRole we need to create a new Windows Azure Cloud Service Project. So select the correct project and in this box we can decide the project name : WindowsAzureTestApplication



After that we have another wizard to select which type of role we want to implement. Select Asp.Net Web Role and WorkerRole.



Next step we can customise the web project. We can chose from Empty, WebForms, Mvc, WebApi, Single Page application and Facebook templates. Select empty template or Mvc.



Now our test project is ready to be customized.



We have three project :
1 - The cloud project WindowsAzureTestApplication
2 - The WebRole project, WebRole
3 - The WorkerRole project, WorkerRole



The cloud project contains three configuration files :
   - ServiceDefinition.csdef
   - ServiceConfiguration.Local.cfcfg
   - ServiceConfiguration.Cloud.cfcfg


The service definition file in our case contains the definition of the WebRole and the WorkerRole. The service configuration files have different configurations for different environments, in this case the local and the cloud environment but we can add other different configuration-environment file. In the cloud project there is also a directory ( Roles ) that contains the configuration of the Role. To change the role configuration we need to open the property like is shown in the following figure.

There are some interesting settings that we can manage. In the configuration tab we can set how many instances of our Role we need. This means that we could have more than one virtual machine per role. This thing is very important because we can improve the availability of the system.
In the settings tab we can put some configurations values typically a connection string to any WindowsAzure services or to a database.



These configurations features are the same for Web and Worker role, but go ahead and we start to modify our role.

For our example we could suppose to download data from a web site that provide us all informations about earth quakes and then create an xml file which contains all this datas. We want to copy this xml in the web site directory and finally our web application will display the data and the updated time.


First of all we need to decide the Role where insert the code to download xml datas from a web site and create the file with all these informations. As We told previously the WorkerRole is used for async calling, long running or loop and independently task so the best place where put our code is the WorkerRole.

 

We start to override the event OnStart and OnStop. In the OnStart event we'll put all the code to initializing our application.

public override bool OnStart()
{
    ServicePointManager.DefaultConnectionLimit = 12;
    ConfigureDiagnostics();
    Trace.TraceInformation("Inizializing our File container !");

    // OUR init code here 

    return base.OnStart();
}


private void ConfigureDiagnostics()
{
    DiagnosticMonitorConfiguration config = DiagnosticMonitor.GetDefaultInitialConfiguration();
    config.ConfigurationChangePollInterval = TimeSpan.FromMinutes(1d);
    config.Logs.BufferQuotaInMB = 500;
    config.Logs.ScheduledTransferLogLevelFilter = Microsoft.WindowsAzure.Diagnostics.LogLevel.Verbose;
    config.Logs.ScheduledTransferPeriod = TimeSpan.FromMinutes(1d);
    DiagnosticMonitor.Start(
            "Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString",
            config
        );
}

 

ServicePointManager.DefaultConnectionLimit sets the maximum number of concurrent connections allowed by a ServicePoint object. The method ConfigureDiagnostics is used to configure diagnostic in our application.

In the OnStop method we will put all code that runs when the role instances is stopped.

public override void OnStop()
{
    onStopCalled = true;
    while (returnedFromRunMethod == false)
        Thread.Sleep(1000);

    base.OnStop();
}

 

When this method is called we set the variable onStopCalled as true to notify at run method that the event stop is called, so the run method can terminate its process and then we shutdown the application. 

The Run method contains the logic to download the XmlFeed and to build another xml with the informations that we need on the web site to display the information.

 

public override void Run()
{
    Trace.TraceInformation("WorkerRoleA entering Run()");

    DateTime lastUpdatedDocument = DateTime.MinValue;

    while (true)
    {
        try
        {
            if (onStopCalled == true)
            {
                Trace.TraceInformation("onStop Called Worker");
                returnedFromRunMethod = true;
                return;
            }

            // Our code
            XElement feed = XElement.Load(EarthQuakeUrl);
            string namespaceName = feed.GetDefaultNamespace().NamespaceName;
            XElement updateDocument = feed.Element(XName.Get("updated", namespaceName));
            DateTime updatedDocument = DateTime.MinValue;
            if (DateTime.TryParse(updateDocument.Value, out updatedDocument) && lastUpdatedDocument < updatedDocument)
            {

                XNamespace ns = XNamespace.Get("http://www.georss.org/georss");
                // Load data into a list of Quake
                List<Quake> quakeList = 
                    (from n in feed.Elements(XName.Get("entry", namespaceName))
                    select new Quake()
                    {
                        Guid = n.Element(XName.Get("id", namespaceName)).Value,
                        Title = CleanField(n.Element(XName.Get("title", namespaceName)).Value, new string[] { "km" }, 1),
                        EventDate = DateTime.Parse(n.Element(XName.Get("updated", namespaceName)).Value),
                        UrlInfo = new Uri(
                                            (from el in n.Elements(XName.Get("link", namespaceName))
                                            where el.Attribute("type").Value == "text/html"
                                            select el.Attribute("href").Value).FirstOrDefault()
                                        ),
                        Coordinate = (from point in n.Elements(XName.Get("point", ns.NamespaceName))
                                        let values = point.Value.Split(' ')
                                        select new Coordinate()
                                        {
                                        Latitude = float.Parse(values[0]),
                                        Longitude= float.Parse(values[1])
                                        }).Single(),
                        Depth = float.Parse(n.Element(XName.Get("elev", ns.NamespaceName)).Value) / 1000,
                        Magnitude = int.Parse((from el in n.Elements(XName.Get("category", namespaceName))
                                            where el.Attribute("label").Value == "Magnitude"
                                            select el.Attribute("term").Value.ToString().Replace("Magnitude ", "")).FirstOrDefault())
                    }).ToList();
                // Create an XML new document
                XDocument newDoc = new XDocument();
                newDoc.Add(
                    new XElement("Quakes",
                        new XElement("title", "Marco Fabbian quake applciation data"),
                        new XElement("updated", updatedDocument.ToString("yyyy-MM-ddTHH:mm:ss")),
                        quakeList.Select(x =>
                        {
                            return
                                new XElement("Quake",
                                    new XElement("id", x.Guid),
                                    new XElement("title", x.Title),
                                    new XElement("eventdate", x.EventDate),
                                    new XElement("urlinfo", x.UrlInfo),
                                    new XElement("coordinate", 
                                                            new XAttribute("Latitude", x.Coordinate.Latitude), 
                                                            new XAttribute("Longitude", x.Coordinate.Longitude)
),
                                    new XElement("depth", x.Depth),
                                    new XElement("magnitude", x.Magnitude)
                                );
                        })
                    )
                    );

                newDoc.Save(Path.Combine(EarthQueakePath, EarthQueake));
                lastUpdatedDocument = updatedDocument;
            }


            // Sleep for one minute to minimize query costs. 
            System.Threading.Thread.Sleep(1000 * 60);
        }
        catch (Exception ex)
        {
            string err = ex.Message;
            if (ex.InnerException != null)
            {
                err += "Inner Exception : " + ex.InnerException.Message;
            }

            Trace.TraceError(err);

            // Don't fill up Trace storage if we have a bug in queue process loop.
            System.Threading.Thread.Sleep(1000 * 60);

        }
    }
}

 

 

All our code is inside try catch statement because if I will have an error I want receive a feedback about the error but I don’t want Windows Azure terminate the role. Morehover we put the code in a cycle because we need to check if there are some update on the list of quakes every 60 second. To read and parse the xml file with datas I use the class XElement and to create the new document  I use XDocument class.


Now We can focus to the WebRole. As we told previously WebRole have the job to check if the parsed xml file exists and if it is updated and then copy into a Web directory. The OnStart, OnStop and Run methods are very similar.

The run method check if the xml file exists and if the file is updated and then copy the xml file in the App_Data directory.

I try to have access to the HttpRequest object but I don’t have access. I have access to the web config to pass the directory path.

 

public override void Run()
{
    Trace.TraceInformation("WorkerRoleA entering Run()");

    DateTime lastUpdatedDocument = DateTime.MinValue;

    while (true)
    {
        try
        {
            if (onStopCalled == true)
            {
                Trace.TraceInformation("onStop Called WorkerB");
                returnedFromRunMethod = true;
                return;
            }

            // Our code
            XElement dataSource = XElement.Load(Path.Combine(ServicePath, EarthQueakeXmlFile));
            string namespaceName = dataSource.GetDefaultNamespace().NamespaceName;

            XElement updateDocument = dataSource.Element(XName.Get("updated", namespaceName));

            DateTime updatedDocument = DateTime.MinValue;

            if (DateTime.TryParse(updateDocument.Value, out updatedDocument) && lastUpdatedDocument < updatedDocument)
            {
                dataSource.Save(Path.Combine(WebPath, "App_Data", EarthQueakeXmlFile));
                lastUpdatedDocument = updatedDocument;

            }

            // Sleep for one minute to minimize query costs. 
            Thread.Sleep(1000 * 60);
        }
        catch (Exception ex)
        {
            string err = ex.Message;
            if (ex.InnerException != null)
            {
                err += "Inner Exception : " + ex.InnerException.Message;
            }

            Trace.TraceError(err);

            // Don't fill up Trace storage if we have a bug in queue process loop.
            System.Threading.Thread.Sleep(1000 * 60);

        }
    }
}

 

 

The rest of the application is very easy to implement. We can use a normal web site and read and display the xml datas or use an MVC project to represent our datas. I select an MvcProject and I modify the home controller. I put the code to read our xml file and I create a ViewModel to show the list of quakes and to display the data of the last update. 

public ViewResult Index()
{
    List<Quake> listQuake = new List<Quake>();
    string webPath = ConfigurationManager.AppSettings["WebPath"].ToString();
    string earthQueakeXmlFile = ConfigurationManager.AppSettings["EarthQueakeXmlFile"].ToString();
    DateTime updatedDocument = DateTime.MinValue;

    string FileDataSource = Path.Combine(webPath, "App_Data", earthQueakeXmlFile);
    if (System.IO.File.Exists(FileDataSource))
    {
        XElement dataSource = XElement.Load(FileDataSource);
        DateTime.TryParse(dataSource.Element(XName.Get("updated")).Value, out updatedDocument);


        listQuake = (from n in dataSource.Elements(XName.Get("Quake"))
                        select new Quake()
                        {
                            Guid = n.Element(XName.Get("id")).Value,
                            Title = n.Element(XName.Get("title")).Value,
                            UrlInfo = new Uri(n.Element(XName.Get("urlinfo")).Value),
                            EventDate = DateTime.Parse(n.Element(XName.Get("eventdate")).Value),
                            Coordinate = (from point in n.Elements("coordinate")
                                        select new Coordinate()
                                        {
                                            Latitude = float.Parse(point.Attribute("Latitude").Value),
                                            Longitude = float.Parse(point.Attribute("Longitude").Value)
                                        }).Single(),
                            Depth = float.Parse(n.Element(XName.Get("depth")).Value),
                            Magnitude = int.Parse(n.Element(XName.Get("magnitude")).Value)
                        }).ToList();
    }
    return View(new QuakeViewModel()
    {
        Quake = listQuake,
        Updated = updatedDocument
    });
}

 

The project is available on the following link : WindowsAzureTestApplication.zip (380.29 kb)


I hope to deal with this argument completely and clearly. I accept all the feedback that you want to send me. Thanks.

Comments (4) -

Yolanda
9/2/2014 4:26:05 AM #

Good post I hope to use more of your information, time willing.

mexico earthquake map
10/6/2014 12:29:03 AM #

It was very interesting to read, thank you.

Rekrutacja odwrocona
12/29/2014 2:54:34 PM #

Spot on with this write-up, I actually suppose this website needs rather more consideration. I´┐Żll probably be once more to read way more, thanks for that info.

byron bay mate
2/10/2015 2:42:13 AM #

Gnarly source of information dude. Love the publish.

Add comment

Calendar

<<  November 2017  >>
MonTueWedThuFriSatSun
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

View posts in large calendar

Page List

    Month List

    AuthorList