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)