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:
- Use Visual Studio to create an empty cloud project.
- Add a worker role to the project (accept the default name of
WorkerRole1
). - Add the following to the
ConfigurationSettings
element ofServiceDefinition.csdef
:<Setting name="EnvironmentChangeString"/> <Setting name="SettingRequiringRecycle"/>
- Add the following to the
ConfigurationSettings
element ofServiceConfiguration.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.
- Add a new class named
EnvironmentChangeExample
to the project. - 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 updatedMicrosoft.WindowsAzure.ServiceRuntime
library. Only with this reference can the user legally use theusing
clauses of the step 6. - 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; }
- 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); } } }
- 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(); }
- Add the following callback method to the class:
private static void RoleEnvironmentStopping(object sender,RoleEnvironmentStoppingEventArgs e) { Trace.TraceInformation("In RoleEnvironmentStopping"); }
- 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; }
- 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
- 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:
- Architecture of the Microsoft Azure Role model http://blogs.msdn.com/b/kwill/archive/2011/05/05/windows-azure-role-architecture.aspx