Book Image

Microsoft Azure Development Cookbook Second Edition

Book Image

Microsoft Azure Development Cookbook Second Edition

Overview of this book

Table of Contents (15 chapters)
Microsoft Azure Development Cookbook Second Edition
Credits
About the Authors
About the Reviewers
www.PacktPub.com
Preface
Index

Handling changes to the configuration and topology of a Cloud Service


A Microsoft Azure Cloud Service has to detect and respond to changes to its service configuration. Two types of changes are exposed to the service: changes to the ConfigurationSettings element of the ServiceConfiguration.cscfg service configuration file and changes to the service topology. The latter refers to changes in the number of instances of the various roles that comprise the service.

The RoleEnvironment class exposes six events to which a role can register a callback method to be notified about these changes:

  • Changing

  • Changed

  • SimultaneousChanging

  • SimultaneousChanged

  • Stopping

  • StatusCheck

The Changing event is raised before the change is applied to the role. For configuration setting changes, the RoleEnvironmentChangingEventArgs parameter to the callback method identifies the existing value of any configuration setting being changed. For a service topology change, the argument specifies the names of any roles whose instance count is changing. The RoleEnvironmentChangingEventArgs parameter has a Cancel property that can be set to true to recycle an instance in response to specific configuration setting or topology changes.

The Changed event is raised after the change is applied to the role. As for the previous event, for configuration setting changes, the RoleEnvironmentChangedEventArgs parameter to the callback method identifies the new value of any changed configuration setting. For a service topology change, the argument specifies the names of any roles whose instance count has changed. Note that the Changed event is not raised on any instance recycled in the Changing event.

The SimulteneousChanging and SimultaneousChanged events behave exactly like the normal events, but they are called only during a simultaneous update.

Tip

These events fire only if we have the topologyChangeDiscovery attribute to Blast in service definition file, for example, <ServiceDefinition name="WAHelloWorld" topologyChangeDiscovery="Blast> as mentioned in the Configuring the service model for a Cloud Service recipe. These events cannot be canceled, and the role will not restart when these events are received. This is to prevent all roles from recycling at the same time.

We will talk about this kind of update in the Publishing a Cloud Service with options from Visual Studio recipe.

The Stopping event is raised on an instance being stopped. The OnStop() method is also invoked. Either of them can be used to implement an orderly shutdown of the instance. However, this must completed within 5 minutes. In a web role, the Application_End() method is invoked before the Stopping event is raised and the OnStop() method is invoked. It can also be used for shutdown code.

Tip

Microsoft Azure takes the instance out of the rotation of the load balancer, and then, it fires the stopping event. This ensures that no shutdown code can execute while legal requests are coming from the Internet.

The StatusCheck event is raised every 15 seconds. The RoleInstanceStatusCheckEventArgs parameter to the callback method for this event specifies the status of the instance as either Ready or Busy. The callback method can respond to the StatusCheck event by invoking the SetBusy() method on the parameter to indicate that the instance should be taken out of the load-balancer rotation temporarily. This is useful if the instance is so busy that it is unable to process additional inbound requests.

In this recipe, we'll learn how to manage service configuration and topology changes to a Cloud Service.

How to do it...

We are going to configure callback methods for four of the six RoleEnvironment events. We will do this by performing the following steps:

  1. Use Visual Studio to create an empty cloud project.

  2. Add a worker role to the project (accept the default name of WorkerRole1).

  3. Add the following to the ConfigurationSettings element of ServiceDefinition.csdef:

    <Setting name="EnvironmentChangeString"/>
    <Setting name="SettingRequiringRecycle"/>
  4. Add the following to the ConfigurationSettings element of ServiceConfiguration.cscfg:

    <Setting name="EnvironmentChangeString"value="OriginalValue"/>
    <Setting name="SettingRequiringRecycle"value="OriginalValue"/>

    Tip

    You can perform steps 3 and 4 with the GUI provided by Visual Studio in the Properties page of the role, under the Settings tab.

  5. Add a new class named EnvironmentChangeExample to the project.

  6. Add the following using statements to the top of the class file:

    using Microsoft.WindowsAzure.ServiceRuntime;
    using System.Collections.ObjectModel;
    using System.Diagnostics;

    Tip

    By adding a WorkerRole to the cloud project during the wizard phase, VS automatically adds a reference to the most updated Microsoft.WindowsAzure.ServiceRuntime library. Only with this reference can the user legally use the using clauses of the step 6.

  7. Add the following callback method to the class:

    private static void RoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e)
    {
      Boolean recycle = false;
      foreach (RoleEnvironmentChange change in e.Changes)
      {
        RoleEnvironmentTopologyChange topologyChange =change as RoleEnvironmentTopologyChange;
        if (topologyChange != null)
        {
          String roleName = topologyChange.RoleName;
          ReadOnlyCollection<RoleInstance> oldInstances =RoleEnvironment.Roles[roleName].Instances;
        }
        RoleEnvironmentConfigurationSettingChange settingChange= change as RoleEnvironmentConfigurationSettingChange;
        if (settingChange != null)
        {
          String settingName =settingChange.ConfigurationSettingName;
          String oldValue =RoleEnvironment.GetConfigurationSettingValue(settingName);
          recycle |= settingName == "SettingRequiringRecycle";
        }
      }
    
      // Recycle when e.Cancel = true;
      e.Cancel = recycle;
    }
  8. Add the following callback method to the class:

    private static void RoleEnvironmentChanged(object sender,RoleEnvironmentChangedEventArgs e)
    {
      foreach (RoleEnvironmentChange change in e.Changes)
      {
        RoleEnvironmentTopologyChange topologyChange =change as RoleEnvironmentTopologyChange;
        if (topologyChange != null)
        {
          String roleName = topologyChange.RoleName;
          ReadOnlyCollection<RoleInstance> newInstances =RoleEnvironment.Roles[roleName].Instances;
        }
        RoleEnvironmentConfigurationSettingChange settingChange= change as RoleEnvironmentConfigurationSettingChange;
        if (settingChange != null)
        {
          String settingName =settingChange.ConfigurationSettingName;
          String newValue =RoleEnvironment.GetConfigurationSettingValue(settingName);
        }
      }
    }
  9. Add the following callback method to the class:

    private static void RoleEnvironmentStatusCheck(object sender,RoleInstanceStatusCheckEventArgs e)
    {
      RoleInstanceStatus status = e.Status;
      // Uncomment next line to take instance out of the// load balancer rotation.
      //e.SetBusy();
    }
  10. Add the following callback method to the class:

    private static void RoleEnvironmentStopping(object sender,RoleEnvironmentStoppingEventArgs e)
    {
      Trace.TraceInformation("In RoleEnvironmentStopping");
    }
  11. Add the following method, associating the callback methods with the RoleEnvironment events, to the class:

    public static void UseEnvironmentChangeExample()
    {
      RoleEnvironment.Changing += RoleEnvironmentChanging;
      RoleEnvironment.Changed += RoleEnvironmentChanged;
      RoleEnvironment.StatusCheck += RoleEnvironmentStatusCheck;
      RoleEnvironment.Stopping += RoleEnvironmentStopping;
    }
  12. If the application is deployed to the local Compute Emulator, the ServiceConfiguration.cscfg file can be modified. It can then be applied to the running service using the following command in the Microsoft Azure SDK command prompt:

    csrun /update:{DEPLOYMENT_ID};ServiceConfiguration.cscfg
  13. If the application is deployed to the cloud, the service configuration can be modified directly on the Microsoft Azure Portal.

How it works...

In steps 1 and 2, we created a cloud project with a worker role. In steps 3 and 4, we added two configuration settings to the service definition file and provided initial values for them in the service configuration file.

In steps 5 and 6, we created a class to house our callback methods.

In step 7, we added a callback method for the RoleEnvironment.Changing event. This method iterates over the list of changes, looking for any topology or configuration settings changes. In the latter case, we specifically look for changes to the SettingRequiringRecycle setting, and on detecting one, we initiate a recycle of the instance.

In step 8, we added a callback method for the RoleEnvironment.Changed event. We iterate over the list of changes and look at any topology changes and configuration settings changes.

Tip

In both the previous steps, we respectively get oldValue and newValue without using them. This is, for example, to get the settings value before and after the changes are made, to eventually use them in a certain situation. However, these events are intended to be used to be notified when particular settings are changed, regardless of which is the actual value before or after the change itself.

In step 9, we added a callback method for the RoleEnvironment.StatusCheck event. We look at the current status of the instance and leave the SetBusy()call commented out, which would take the instance out of the load balancer rotation.

In step 10, we added a callback method for the RoleEnvironment.Stopping event. In this callback, we used Trace.TraceInformation() to log the invocation of the method.

In step 11, we added a method that associated the callback methods with the appropriate event.

In step 12, we saw how to modify the service configuration in the development environment. We must replace {DEPLOYMENT_ID} with the deployment ID of the current deployment. The deployment ID in the Computer Emulator is a number that is incremented with each deployment. It is displayed on the Compute Emulator UI. In step 13, we saw how to modify the service configuration in a cloud deployment.

There's more...

The RoleEntryPoint class also exposes the following virtual methods that allow various changes to be handled:

  • RoleEntryPoint.OnStart()

  • RoleEntryPoint.OnStop()

  • RoleEntryPoint.Run()

These virtual methods are invoked when an instance is started, stopped, or when it reaches a Ready state. An instance of a worker role is recycled whenever the Run() method exits.

Testing changes with the SDK command line

The csrun command in the Microsoft Azure SDK can be used to test configuration changes in the development fabric. The service configuration file can be modified, and csrun can be invoked to apply the change. Note that it is not possible to test topology changes that reduce the number of instances. However, when the Cloud Service is started without debugging, it is possible to increase the number of instances by modifying the service configuration file and using csrun.

Using LINQ with the RoleEnvironment API

As both RoleEnvironmentChanging and RoleEnvironmentChanged use the RoleEnvironment APIs to check collections, we can also simplify the code in steps 7 and 8 with new LINQ-based implementations as follows:

private static void RoleEnvironmentChanging(object sender, 
    RoleEnvironmentChangingEventArgs e)
{            
    var oldInstances =
        e.Changes.OfType<RoleEnvironmentTopologyChange>()
        .SelectMany(p => RoleEnvironment.Roles[p.RoleName].Instances);
    var oldValues =        e.Changes.OfType<RoleEnvironmentConfigurationSettingChange>()
        .ToDictionary(p => p.ConfigurationSettingName,p=>RoleEnvironment            .GetConfigurationSettingValue(p.ConfigurationSettingName));
    e.Cancel =oldValues.Any(p=>p.Key=="SettingRequiringRecycle");
}

In the code mentioned earlier, we group the old changing instances and the old settings' key-value pairs. In the last line, we recycle the SettingRequiringRecycle setting, if there is any.

Step 8 can be modified as mentioned earlier, but by finding new instances and settings' values instead of old ones, while asking the RoleEnvironment APIs about them.

See also

Have a look at the following MSDN blog post to get additional information: