Implementando um barramento de eventos com o RabbitMQ para o ambiente de desenvolvimento ou de teste

Dica

Esse conteúdo é um trecho do eBook da Arquitetura de Microsserviços do .NET para os Aplicativos .NET em Contêineres, disponível no .NET Docs ou como um PDF para download gratuito que pode ser lido offline.

Imagem em miniatura da capa do eBook da Arquitetura de Microsserviços do .NET para os Aplicativos .NET em Contêineres.

Devemos começar dizendo que, se você criar seu barramento de eventos personalizado com base no RabbitMQ em execução em um contêiner, como o aplicativo eShopOnContainers faz, ele deverá ser usado apenas para seus ambientes de desenvolvimento e teste. Não o use para seu ambiente de produção, a menos que você o esteja criando como parte de um barramento de serviço pronto para produção, conforme descrito na seção Recursos adicionais abaixo. Um barramento de eventos personalizado simples talvez não tenha muitos recursos críticos prontos para produção que um barramento de serviço comercial tem.

Uma das implementações personalizadas do barramento de eventos no eShopOnContainers é basicamente uma biblioteca usando a API do RabbitMQ. (Há outra implementação baseada no Barramento de Serviço do Azure.)

A implementação do barramento de eventos com RabbitMQ permite que os microsserviços assinem, publiquem e recebam eventos, conforme mostrado na Figura 6-21.

Diagrama mostrando o RabbitMQ entre o remetente e o destinatário da mensagem.

Figura 6-21. Implementação de um barramento de eventos do RabbitMQ

O RabbitMQ é um intermediário entre o publicador da mensagem e os assinantes, para tratar da distribuição. No código, a classe EventBusRabbitMQ implementa a interface IEventBus genérica. Essa implementação é baseada na injeção de dependência para que seja possível alternar dessa versão de desenvolvimento/teste para uma versão de produção.

public class EventBusRabbitMQ : IEventBus, IDisposable
{
    // Implementation using RabbitMQ API
    //...
}

A implementação do RabbitMQ de um barramento de eventos de Desenvolvimento/Teste de exemplo é um código clichê. Ele precisa lidar com a conexão com o servidor do RabbitMQ e fornecer o código para a publicação de um evento de mensagem nas filas. Ele também precisa implementar um dicionário de coleções de manipuladores de eventos de integração para cada tipo de evento; esses tipos de evento podem ter uma instanciação diferente e diferentes assinaturas para cada microsserviço receptor, conforme mostrado na Figura 6-21.

Implementando um método de publicação simples com RabbitMQ

O código a seguir é uma versão simplificada de uma implementação de barramento de eventos para RabbitMQ, para mostrar todo o cenário. Na prática, a conexão não é tratada dessa maneira. Para ver a implementação completa, confira o código real no repositório dotnet-architecture/eShopOnContainers.

public class EventBusRabbitMQ : IEventBus, IDisposable
{
    // Member objects and other methods ...
    // ...

    public void Publish(IntegrationEvent @event)
    {
        var eventName = @event.GetType().Name;
        var factory = new ConnectionFactory() { HostName = _connectionString };
        using (var connection = factory.CreateConnection())
        using (var channel = connection.CreateModel())
        {
            channel.ExchangeDeclare(exchange: _brokerName,
                type: "direct");
            string message = JsonConvert.SerializeObject(@event);
            var body = Encoding.UTF8.GetBytes(message);
            channel.BasicPublish(exchange: _brokerName,
                routingKey: eventName,
                basicProperties: null,
                body: body);
       }
    }
}

O código real do método Publish no aplicativo eShopOnContainers é aprimorado usando uma política de repetição Polly, que repetirá a tarefa algumas vezes se o contêiner do RabbitMQ não estiver pronto. Esse cenário poderá ocorrer quando o docker-compose estiver iniciando os contêineres, por exemplo, o contêiner do RabbitMQ poderá iniciar mais lentamente do que os outros contêineres.

Conforme mencionado anteriormente, há muitas configurações possíveis no RabbitMQ. Portanto, esse código deve ser usado apenas para ambientes de Desenvolvimento/Teste.

Implementando o código de assinatura com a API do RabbitMQ

Como acontece com o código de publicação, o código a seguir é uma simplificação de parte da implementação do barramento de eventos para RabbitMQ. Mais uma vez, geralmente não é necessário alterá-lo, a menos que você o esteja aprimorando.

public class EventBusRabbitMQ : IEventBus, IDisposable
{
    // Member objects and other methods ...
    // ...

    public void Subscribe<T, TH>()
        where T : IntegrationEvent
        where TH : IIntegrationEventHandler<T>
    {
        var eventName = _subsManager.GetEventKey<T>();

        var containsKey = _subsManager.HasSubscriptionsForEvent(eventName);
        if (!containsKey)
        {
            if (!_persistentConnection.IsConnected)
            {
                _persistentConnection.TryConnect();
            }

            using (var channel = _persistentConnection.CreateModel())
            {
                channel.QueueBind(queue: _queueName,
                                    exchange: BROKER_NAME,
                                    routingKey: eventName);
            }
        }

        _subsManager.AddSubscription<T, TH>();
    }
}

Cada tipo de evento tem um canal relacionado para obter eventos do RabbitMQ. Em seguida, é possível ter tantos manipuladores de eventos por tipo de evento e de canal conforme necessário.

O método Subscribe aceita um objeto IIntegrationEventHandler, que é como um método de retorno de chamada no microsserviço atual, além de seu objeto IntegrationEvent relacionado. O código, então, adiciona o manipulador de eventos à lista de manipuladores de eventos que cada tipo de evento de integração pode ter por microsserviço do cliente. Se o código do cliente ainda não tiver assinado o evento, o código criará um canal para o tipo de evento para que ele possa receber eventos em um estilo de push do RabbitMQ quando esse evento for publicado de qualquer outro serviço.

Conforme mencionado acima, o barramento de eventos implementado no eShopOnContainers tem apenas uma finalidade educacional, pois trata apenas dos cenários principais, portanto, não está pronto para produção.

Para cenários de produção, verifique os recursos adicionais abaixo, específicos para RabbitMQ, e a seção Implementar comunicação baseada em eventos entre microsserviços.

Recursos adicionais

Uma solução pronta para produção com suporte para RabbitMQ.