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.
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).
Inicie essa aplicação também para ver se tudo está funcionando. Se estiver, você deverá ver algo como à seguir.
download da solução (10 Kb)
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); }
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)