Service Bus En Windows Azure Platform AppFabric

Después de la introducción en el post anterior a AppFabric, me arriesgo a adentrarme un poco más en cada uno de los elementos que la componen. A continuación, voy a centrarme en Service Bus y en cómo podemos crear un pequeño ejemplo para involucrarlo.

En primer lugar, cabe mencionar el patrón ESB (Enterprise Service Bus)  para poder entender el mecanismo. De una forma simplificada, podemos decir que este patrón está basado en mensajes y unas normas a seguir por los integrantes en la comunicación. Necesitamos un sistema por el cual seamos capaces de enviar un mensaje o petición al otro extremo, siendo conscientes de la existencia de firewalls, dispositivos NATs, etc. ¿Y si existiera la posibilidad de contar con un intermediario que enviara por nosotros ese mensaje? Aquí es donde entra en juego Service Bus.

En la actualidad, existen SDK’s para .NET, Java y Ruby. En .NET nos resultará bastante familiar si alguna vez hemos trabajado con WCF. Para poder comenzar en nuestro entorno de Visual Studio, es necesario bajar el siguiente SDK para .NET. Además, antes de empezar a trabajar con Service Bus, debemos completar los pasos citados en el anterior post desde el portal de AppFabric.

En este post, voy a crear tres aplicaciones: Un servicio con WCF, que será la forma de comunicarse una aplicación con otra, un proyecto de consola que actuará como servidor y una aplicación winform que enviará y recibirá mensajes.

CREAR UN SERVICIO CON WCF

En primer lugar, necesitamos generar un contrato y su implementación como si de servicio web se tratara. Para ello, he creado un proyecto de tipo WCF Service Application llamado ServiceBusAppFabric.

Renombro la interfaz IService1.cs por IServiceBus.cs y la actualizo con el siguiente código:

using System.Runtime.Serialization;
using System.ServiceModel;

namespace ServiceBusAppFabric
{
[ServiceContract]
public interface IServiceBus
{
[OperationContract]
void SendMessage(string msg, string sender, string receiver);

[OperationContract]
CompositeType ReceiveMessage(string receiver);
}


[DataContract]
public class CompositeType
{
[DataMember]
public string Sender { get; set; }

[DataMember]
public string InstantMessage { get; set; }
}
}

Modifico el nombre de Service1.svc por ServiceBus.svc e implemento la interfaz que acabamos de crear.

using System;
using System.Linq;

namespace ServiceBusAppFabric
{
public class ServiceBus : IServiceBus
{
public void SendMessage(string msg, string sender, string receiver)
{
var imEntities = new InstantMessagesDB();
MessageIM iM = new MessageIM { InstantMessage = msg, Receiver = receiver, Sender = sender };
imEntities.AddToMessageIMSet(iM);
imEntities.SaveChanges();
Console.WriteLine("Received Message from:" + sender);
}

public CompositeType ReceiveMessage(string receiver)
{
var imEntities = new InstantMessagesDB();
var IMs = (from i in imEntities.MessageIMSet.ToList()
where i.Receiver == receiver
select i).FirstOrDefault();
if (IMs != null)
{
Console.WriteLine(string.Format("Sending Message: {0} from {1}", IMs.InstantMessage, IMs.Sender));
return new CompositeType { InstantMessage = IMs.InstantMessage, Sender = IMs.Sender };
}
return new CompositeType { InstantMessage = "No Messages", Sender = "No one" };
}
}
}

Como podemos ver, en la llamada SendMessage estoy recibiendo el mensaje, el remitente y el destinatario del mismo. En esta demo estoy utilizando Entity Framework para acceder a una base de datos en SQL Express llamada IM con una tabla como esta:

Creo un objeto del tipo MessageIM y lo guardo en base de datos. Por último, muestro un mensaje por consola informando del nuevo mensaje recibido.
El método ReceiveMessage recibe el nombre del destinatario y busca entre los mensajes guardados cual le corresponde. De no obtener ningún resultado informa al usuario que nadie envió nada para él.

CREACIÓN DEL SERVIDOR

Una vez que tenemos la forma de comunicarnos, necesitamos crear una aplicación que tenga el rol de servidor. Para ello, he creado la siguiente aplicación de consola:

using System.ServiceModel;
using ServiceBusAppFabric;

namespace ServerAppFabric
{
class Program
{
static void Main(string[] args)
{
System.Console.WriteLine("Waiting a response from AppFabric...");
ServiceHost host = new ServiceHost(typeof(ServiceBus));
host.Open();

System.Console.WriteLine("I'm ready =)");
System.Console.WriteLine("Press [Enter] to exit");
System.Console.ReadLine();

host.Close();
}
}
}

Es necesario importar la dll que generó nuestro proyecto de WCF y además System.ServiceModel para poder crear un objeto de tipo ServiceHost.

Ahora lo que necesitamos es configurar un endpoint de la misma manera que podríamos hacerlo para un servicio en WCF, con algunos pequeños cambios. Añadimos un archivo de tipo App.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="InstantMessagesDB" connectionString="metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=.SQLEXPRESS;Initial Catalog=IM;Integrated Security=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />
</connectionStrings>
<system.serviceModel>
<services>
<service name="ServiceBusAppFabric.ServiceBus" >
<endpoint address="sb://gisendpoint.servicebus.windows.net/ServiceBus"
behaviorConfiguration ="sharedSecretClientCredentials"
binding="netTcpRelayBinding"
contract="ServiceBusAppFabric.IServiceBus" />
</service>
</services>
<behaviors>
<endpointBehaviors >
<behavior name ="sharedSecretClientCredentials">
<transportClientEndpointBehavior credentialType="SharedSecret">
<clientCredentials>
<sharedSecret issuerName="[Issuer Name]"
issuerSecret="[Issuer Key]"/>
</clientCredentials>
</transportClientEndpointBehavior>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>

Dentro de la sección configuration tenemos un connectionString, para poder conectar con la base de datos IM, y  la sección system.serviceModel con la configuración necesaria para la parte services. En el post anterior, comentaba que era posible visualizar el detalle del namespace del servicio que acabamos de crear. Si volvemos a aquel apartado podemos localizar los valores necesarios para completar el endpoint.

  • En la propiedad address añadiremos sb://serviceNamespace.servicebus.windows.net
  • El binding  será de tipo netTcpRelayBinding. Para poder utilizar este tipo de binding sólo es necesario tener en cuenta que utiliza SSL a través del puerto 828. Por este motivo, es necesario abrir dicho puerto en nuestro router para que pueda funcionar correctamente.
  • El contrato sería la interfaz de nuestro servicio WCF creado anteriormente

Para finalizar, hemos creado un behavior un tanto especial. Esta información también es particular del namespace del servicio que creamos en AppFabric y serán necesarios tanto Default Issuer Name como Default Issuer Key.

Si arrancamos la aplicación, vemos que en un primer momento espera la contestación por parte de AppFabric y, a los pocos segundos, está listo para su uso.

CREACIÓN DEL CLIENTE

La última aplicación pendiente sería un cliente winform con el siguiente aspecto:

En realidad es bastante simple. La primera parte sería para el envío de mensajes, escribiendo tanto el remitente, como el destinatario y el mensaje en sí, y en la parte inferior podemos pasarle el nombre del usuario del cual queremos recuperar el mensaje pendiente.

using System.ServiceModel;
using System.Windows.Forms;
using ServiceBusAppFabric;

namespace ClientForm
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void btnSend_Click(object sender, System.EventArgs e)
{
ChannelFactory<IServiceBus> channel = new ChannelFactory<IServiceBus>("RelayEndpoint");
IServiceBus client = channel.CreateChannel();

client.SendMessage(txtMessage.Text, txtSender.Text, txtReceiver.Text);

lblResultSend.Text = "Sent It!";

channel.Close();
}

private void btnReceived_Click(object sender, System.EventArgs e)
{
ChannelFactory<IServiceBus> channel = new ChannelFactory<IServiceBus>("RelayEndpoint");
IServiceBus client = channel.CreateChannel();
var result = client.ReceiveMessage(txtReceiverName.Text);

lblMessagesResult.Text = "Message: " + result.InstantMessage + " From:" + result.Sender;

channel.Close();
}
}
}

En el codebehind del formulario tenemos un evento para cada botón. En ambos, abrimos un canal a través de ChannelFactory especificando el contrato de nuestro servicio y utilizamos el envío o la recepción de mensajes según el caso. El endpoint del cliente sería similar al anterior.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint address ="sb://gisendpoint.servicebus.windows.net/ServiceBusAppFabric"
binding="netTcpRelayBinding"
contract="ServiceBusAppFabric.IServiceBus"
behaviorConfiguration="sharedSecretClientCredentials"
name="RelayEndpoint" />
</client>
<behaviors>
<endpointBehaviors >
<behavior name="sharedSecretClientCredentials">
<transportClientEndpointBehavior credentialType="SharedSecret">
<clientCredentials>
<sharedSecret issuerName="[Issue Name]"
issuerSecret="[Issuer Secret]" />
</clientCredentials>
</transportClientEndpointBehavior>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>

Si arrancamos el servidor y acto seguido el cliente, comprobaríamos que efectivamente podemos enviar y recibir mensajes:

Incluyo los tres proyectos a este post.

¡Saludos!

6 comentarios en “Service Bus En Windows Azure Platform AppFabric”

  1. Hola Gisela nuevamente saludos, gracias por agregar el codigo de ejemploi, porque lo intente hacer por mi cuenta y no me dio =(

    espero que sigas ayudando a todos con tu conocimiento, felicidades!!

  2. Hola Paco,

    En primer lugar, muchas gracias por tu comentario y mis disculpas por tardar en contestar.

    ¿Recuerdas qué problemas encontraste por el camino? Quizás sería interesante verlo =)

    ¡Saludos!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *