How to leverage the internal HTTP endpoint available on Azure web roles?
- by Alfredo Delsors
Imagine you have a Web application using an in-memory collection that changes occasionally but is used very often. The collection gets loaded from storage on the Application_Start global.asax event and is updated whenever its content changes.
If you want to deploy this application on Azure you need to keep in mind that more than one instance of the application can be running at any time and therefore you need to provide some mechanism to keep all instances informed with the latest changes.
Because the communication through internal endpoints between Azure role instances is at no cost, a good solution can be maintaining the information on Azure Storage Tables, reading its contents on the Application_Start event and populating its changes to all other instances using the internal HTTP port available on Azure Web Roles.
You need to follow these steps to leverage the internal HTTP endpoint available on Azure web roles to maintain all instances up to date.
1. Define an internal HTTP endpoint in the Web Role properties, for example InternalHttpEndpoint
2. Add a new WCF service to the Web Role, for example NotificationService.svc
3. Disable multiple site bindings in web.config:
<serviceHostingEnvironment multipleSiteBindingsEnabled="false">
4. Add a method on the new service to receive notifications from other role instances.
namespace Service
{
[ServiceContract]
public interface INotificationService
{
[OperationContract(IsOneWay = true)]
void Notify(Information info);
}
}
5. Declare a class that inherits from System.ServiceModel.Activation.ServiceHostFactory and override the method CreateServiceHost to host the internal endpoint.
public class InternalServiceFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
var internalEndpointAddress = string.Format(
"http://{0}/NotificationService.svc",
RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["InternalHttpEndpoint"].IPEndpoint);
ServiceHost host = new ServiceHost(
typeof(NotificationService),
new Uri(internalEndpointAddress));
BasicHttpBinding binding = new BasicHttpBinding(SecurityMode.None);
host.AddServiceEndpoint(
typeof(INotificationService),
binding,
internalEndpointAddress);
return host;
}
}
Note that you can use SecurityMode.None because the internal endpoint is private to the instances of the service.
6. Edit the markup of the service right clicking the svc file and selecting "View markup" to add the new factory as the factory to be used to create the service
<%@ ServiceHost Language="C#" Debug="true" Factory="Service.InternalServiceFactory" Service="Service.NotificationService" CodeBehind="NotificationService.svc.cs" %>
7. Now you can notify changes to other instances using this code:
var current = RoleEnvironment.CurrentRoleInstance;
var endPoints = current.Role.Instances
.Where(instance => instance != current)
.Select(instance => instance.InstanceEndpoints["InternalHttpEndpoint"]);
foreach (var ep in endPoints)
{
EndpointAddress address = new EndpointAddress(
String.Format("http://{0}/NotificationService.svc",
ep.IPEndpoint));
BasicHttpBinding binding = new BasicHttpBinding(SecurityMode.None);
var factory = new ChannelFactory<INotificationService>(binding);
INotificationService instance = factory.CreateChannel(address);
instance.Notify(changedinfo);
}