Supongamos que tenemos una aplicación Web utilizando una colección in-memory que cambia ocasionalmente pero que se utiliza muy a menudo. La colección se cargaría desde tablas Azure en el arranque de la aplicación, utilizando el evento Application_Start de global.asax, y se actualizaría cuando la aplicación actualizara la tabla, de forma que las consultas podrían realizarse sobre la colección en memoria.
Si vamos a desplegar la en Azure habrá que tener presente que se ejecutará más de una instancia de la aplicación en cada momento y en este caso el mecanismo descrito no sirve, las instancias no comparten una única colección.
Una opción para solucionar el problema sería utilizar Windows Azure AppFabric Caching para almacenar la colección. Cuando desde alguna instancia se modificara el valor de la colección, se actualizaría en la cache distribuida que comparten todas las instancias. Si las consultas van a ser muy frecuentes a lo mejor resulta que la factura que habremos de pagar para poder utilizar esta caché acaba siendo demasiado elevada, teniendo en cuenta que cada consulta contará como una transacción.
Pensando en que la comunicación entre los endpoints internos es gratis, una alternativa podría ser mantener la información en tablas de Azure, leer su contenido con el mismo evento anterior y comunicar los cambios que se produzcan en cada instancia al resto de instancias utilizando el endpoint interno HTTP disponible en los Web Roles de Azure.
Para hacerlo habría que seguir los siguientes pasos:
1. Definir un endpoint interno HTTP en las propiedades del Web Role, por ejemplo InternalHttpEndpoint
.png)
2. Añadir un nuevo servicio WCF al Web Role, por ejemplo NotificationService.svc
3. Deshabilitar multiple site bindings en el web.config:
<serviceHostingEnvironment multipleSiteBindingsEnabled="false">
4. Añadir un método en el nuevo servicio para recibir notificaciones desde las otras instancias del Rol.
namespace Service {
[ServiceContract]
public interface INotificationService {
[OperationContract(IsOneWay = true)]
void Notify(Information info);
}}
5. Declarar una clase que herede de System.ServiceModel.Activation.ServiceHostFactory y sobreescribir el método CreateServiceHost para hospedar el endpoint interno.
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;
}
}
Nota: se puede utilizar SecurityMode.None porque los endpoints internos son privados a las instancias del servicio, no son accesibles desde el exterior y por lo tanto completamente seguros.
6. Editar el markup del servicio pulsando con el botón derecho del ratón sobre el fichero svc y seleccionando "View markup" para añadir la factoría anterior como la factoría que debe utilizarse para crear instancias del servicio.
<%@ ServiceHost Language="C#" Debug="true" Factory="Service.InternalServiceFactory" Service="Service.NotificationService" CodeBehind="NotificationService.svc.cs" %>
7. Ahora se pueden notificar los cambios a otras instancias como sigue:
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);
}
Si el número de consultas sobre la colección puede llegar a ser muy grande, el coste de implementación de esta aproximación puede resultar rentable.