quinta-feira, 13 de dezembro de 2012

MSMQ: Pub/Sub

Esse post é a continuação de uma pequena série de posts sobre padrões básicos de integração usando o MSMQ com C#. Dessa vez, vou demonstrar como podemos implementar o padrão Pub/Sub, que também é conhecido como Publish/Subscribe. Esse padrão é mais ou menos uma versão entre sistemas do padrão Observer que encontramos na orientação a objetos. Pub/Sub é o padrão fundamental de uma Arquitetura Orientada a Eventos.

O Publisher notifica sobre a ocorrência de um evento sem saber quem ou até mesmo se existe algum Subscriber (também conhecido como inscrito, interessado, subscrito, etc. [nunca achei uma boa tradução para esse termo]) que irá recebê-lo.

Uma analogia a esse padrão seria a transmissão de um programa de rádio, por exemplo. A antena de transmissão simplesmente irradia o sinal sem saber quem irá sintonizá-lo -- esse seria o Publisher. Os subscribers, seriam os ouvintes.


Chega de teoria e vamos à prática!

Pré-Requisitos

Se você leu algum dos posts anteriores, sabe quais são os pré-requisitos. Caso contrário, dê uma olhada aqui na seção Pré-Requisitos.

Prática

O primeiro passo é criarmos um modelo canônico que será referenciado pelos participantes da solução. Sendo assim, crie um projeto do tipo class library em C# de nome Eip.Messages. Declare a seguinte interface seguida de uma implementação padrão.

public interface IEventOccurred
{
 string Event { get; set; }
}

[Serializable]
public class EventOccurredImpl
 : IEventOccurred
{
 public EventOccurredImpl(string @event)
 {
  Event = @event;
 }
 public string Event { get; set; }
}

Agora, adicione um novo projeto à solução do tipo Console de nome Eip.Publisher e adicione referências a System.Messaging e ao projeto que acabamos de criar. Em seguida, cole o trecho de código abaixo dentro do método static void Main(string[] args).

Console.Title = "Publisher Endpoint";
Console.WindowHeight = 10;
Console.WindowWidth = 50;

// Instancia a fila da publicação/notificação (publish)
MessageQueue publishQueue = new MessageQueue("FormatName:MULTICAST=236.1.1.1:8001");

while (true)
{
 Console.WriteLine("Entre com a descrição do evento ocorrido:");
 string inputEvent = Console.ReadLine();

 if (inputEvent == "sair")
  break;

 // Instancia a notificação
 IEventOccurred request = new EventOccurredImpl(inputEvent);

 // Instancia a mensagem que irá encapsular nossa notificação
 Message publishMessage = new Message(request, new BinaryMessageFormatter());

 // Envia a requisição
 publishQueue.Send(publishMessage);
 
 Console.WriteLine("Evento publicado: '{0}'", inputEvent);
}

Inicie a aplicação para ver se tudo está correto. Se tiver, vamos para o próximo passo,


Agora, adicione mais um projeto do tipo Console à solução com nome Eip.Subscriber. Aqui também, adicione referências a System.Messaging e a Eip.Messages. Em seguida, cole o trecho de código abaixo dentro do método static void Main(string[] args).


Console.Title = "Subscriber Endpoint";
Console.WindowHeight = 10;
Console.WindowWidth = 50;

// Cria uma fila exclusiva privada para esse subscriber
Console.WriteLine("Entre com o nome desse subscriber (apenas letras e underscore):");
string subscriberQueueName = Console.ReadLine();

string subscriberQueuePath = string.Format(@".\private$\{0}", subscriberQueueName);

MessageQueue subscribeQueue = null;

// Verifica se a fila já existe
// Se existir, instancia a fila; caso contrário, cria uma nova
if (MessageQueue.Exists(subscriberQueuePath))
 subscribeQueue = new MessageQueue(subscriberQueuePath);
else
 subscribeQueue = MessageQueue.Create(subscriberQueuePath);

// Note que estamos usando o mesmo IP declarado no Publisher
// Fazendo isso, estamos dizendo que a nossa fila irá receber as notificações Multicast
// recebidas nesse endereço.
subscribeQueue.MulticastAddress = "236.1.1.1:8001";

subscribeQueue.Formatter = new BinaryMessageFormatter();

Console.WriteLine("Aguardando notificações...");

// Loop principal para ouvir as notificações
while (true)
{
 Message notificationMessage = subscribeQueue.Receive();
 IEventOccurred eventReceived = (IEventOccurred)notificationMessage.Body;
 Console.WriteLine("Notificação recebida: '{0}'", eventReceived.Event);
}


Inicie essa aplicação também para ver se tudo está funcionando. Se estiver, você deverá ver algo como à seguir.


Para que esse tutorial faça sentido, inicie uma instância de Eip.Publisher e duas ou mais instâncias de Eip.Subscriber (entrando com nomes diferentes). O vídeo à seguir mostra como iniciar um Publisher e três Subscribers.


Conclusão

Com esse post, encerro a série de padrões elementares de uma arquitetura usando um sistema de mensagens como infraestrutura -- MSMQ. O padrão Pub/Sub em especial, é a alma de uma Arquitetura Orientada a Eventos. Vale ressaltar que essa abordagem, de forma alguma, visa substituir uma SOA; é um complemento. Mesmo porque SOA não é uma técnica, é um conceito -- você pode muito bem disponibilizar serviços em formato de mensagens.

Como já mencionado no primeiro post, foi proposital o nível de hard code e falta de abstração. Da mesma forma que você usaria uma interface de acesso a dados em uma camada de negócio, você também usaria uma abstração para o acesso à infraestrutura do sistema de mensageria em um cenário real.

Se você se interessou pelo assunto, eu recomendo dar uma olhada no sistema de mensagens RabbitMQ. O RabbitMQ é muito superior ao MSMQ em muitas questões. O MSMQ tem basicamente suporte a enviar e receber mensagens, enquanto que o RabbitMQ possui um suporte robusto a roteamento de mensagens, e ainda por cima, implementa 100% o protocolo AMQP.

Se você já estiver craque nos conceitos de uma EDA e em algum sistema de mensagens, talvez o próximo passo seja começar a usar um framework específico para esse tipo de arquitetura. Recomendo dar uma olhada em três deles:

  • NServiceBus - É o mais robusto, maduro e com boa documentação. Tem suporte ao MSMQ e RabbitMQ.
  • MassTransit - Funciona bem, porém um pouco experimental e com uma documentação ruim. A curva de aprendizado é um pouco árdua. Também tem suporte ao MSMQ e RabbitMQ.
  • EasyNetQ - Só possui suporte ao RabbitMQ, mas é realmente bom e muito (muito mesmo) fácil de aprender. A documentação é boa e eu até contribuí com a tradução dela para português.
Os três frameworks são open source, porém o NServiceBus tem um modelo pago. Por isso, leia bem as questões de licença antes de pensar em implementar um projeto com ele -- você não pode simplesmente sair o usando em produção.

Obrigado por visitar o blog!


download da solução (10 Kb)

Nenhum comentário:

Postar um comentário