Comunicação assíncrona baseada em mensagens

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.

.NET Microservices Architecture for Containerized .NET Applications eBook cover thumbnail.

As mensagens assíncronas e a comunicação controlada por eventos são críticas ao propagar alterações em vários microsserviços e seus modelos de domínio relacionados. Conforme mencionado anteriormente na discussão, microsserviços e BCs (Contextos Limitados), modelos (Usuário, Cliente, Produto, Conta etc.) podem ter diferentes significados para diferentes microsserviços ou BCs. Isso significa que, quando ocorrem alterações, você precisa de algum modo de reconciliar essas alterações entre diferentes modelos. Uma solução é consistência eventual e comunicação controlada por evento com base em mensagens assíncronas.

Ao usar mensagens, processos se comunicam trocando mensagens de maneira assíncrona. Um cliente faz um comando ou uma solicitação a um serviço ao enviar uma mensagem a ele. Se o serviço precisar responder, ele enviará uma mensagem diferente de volta ao cliente. Como é uma comunicação baseada em mensagens, o cliente presume que a resposta não será recebida imediatamente e que poderá não haver resposta alguma.

Uma mensagem é composta por um cabeçalho (metadados como informações de identificação ou de segurança) e um corpo. As mensagens geralmente são enviadas por meio de protocolos assíncronos, como AMQP.

A infraestrutura preferencial para esse tipo de comunicação na comunidade de microsserviços é um agente de mensagem leve, que é diferente de agentes e orquestradores grandes usados em SOA. Em um agente de mensagens leve, a infraestrutura é normalmente "burra", atuando somente como agente de mensagens, com implementações simples, como RabbitMQ ou um Barramento de Serviço escalonável na nuvem, como Barramento de Serviços do Azure. Nesse cenário, a maioria do pensamento "inteligente" ainda está nos pontos de extremidade que estão produzindo e consumindo mensagens; ou seja, nos microsserviços.

Outra regra que você deve tentar seguir o máximo possível é usar apenas mensagens assíncronas entre os serviços internos e usar comunicação síncrona (como HTTP) apenas dos aplicativos cliente para os serviços de front-end (gateways de API mais o primeiro nível de microsserviços).

Há dois tipos de comunicação assíncrona de mensagens: comunicação baseada em mensagens do receptor único e comunicação baseada em mensagens de vários receptores. As seções a seguir fornecem detalhes sobre eles.

Comunicação baseada em mensagem de destinatário único

Comunicação assíncrona baseada em mensagem com um único destinatário significa que há comunicação ponto a ponto que entrega uma mensagem a exatamente um dos consumidores que está lendo do canal e que essa mensagem é processada apenas uma vez. No entanto, existem situações especiais. Por exemplo, em um sistema de nuvem que tenta se recuperar automaticamente de falhas, a mesma mensagem poderia ser reenviada várias vezes. Devido a falhas na rede ou outras falhas, o cliente precisa ser capaz de repetir o envio de mensagens, e o servidor precisa implementar uma operação que seja idempotente para processar uma mensagem específica apenas uma vez.

A comunicação baseada em mensagem destinatário único é especialmente adequada para enviar comandos assíncronos de um microsserviço para outro, conforme mostrado na Figura 4-18 que ilustra essa abordagem.

Depois de começar a enviar comunicação baseada em mensagens (seja com comandos ou eventos), você deve evitar misturar comunicação baseada em mensagem com comunicação HTTP síncrona.

A single microservice receiving an asynchronous message

Figura 4-18. Um único microsserviço recebendo uma mensagem assíncrona

Quando os comandos provém de aplicativos cliente, eles podem ser implementados como comandos síncronos HTTP. Use comandos baseados em mensagens quando precisar de maior escalabilidade ou quando já estiver em um processo de negócios baseado em mensagens.

Comunicação baseada em mensagens de vários destinatários

Como uma abordagem mais flexível, você também pode desejar usar um mecanismo publicação/assinatura para que sua comunicação do remetente fique disponível para microsserviços de assinantes adicionais ou para aplicativos externos. Portanto, ele o ajuda a seguir o princípio de abrir/fechar no serviço de envio. Dessa forma, mais assinantes podem ser adicionados no futuro sem necessidade de modificar o serviço do remetente.

Ao usar uma comunicação de publicação/assinatura, talvez você esteja usando uma interface de barramento de eventos para publicar eventos a qualquer assinante.

Comunicação controlada por evento assíncrono

Ao usar comunicação controlada por evento assíncrono, um microsserviço publica um evento de integração quando acontece algo em seu domínio e outro microsserviço precisa estar ciente disso, como uma alteração de preço em um microsserviço do catálogo de produtos. Microsserviços adicionais assinam os eventos para que possam recebê-los de maneira assíncrona. Quando isso acontece, os receptores podem atualizar as próprias entidades de domínio, o que podem causar a publicação de mais eventos de integração. Este sistema de publicação/assinatura é executado por meio da implementação de um barramento de evento. O barramento de evento pode ser projetado como uma abstração ou interface, com a API que é necessária para assinar ou cancelar a assinatura de eventos e para publicar eventos. O barramento de evento também pode ter uma ou mais implementações com base em qualquer agente de mensagens e entre processos, como uma fila de mensagens ou barramento de serviço que seja compatível com a comunicação assíncrona e um modelo de publicação/assinatura.

Se um sistema usa consistência eventual orientada por eventos de integração, é recomendável que essa abordagem fique completamente clara para o usuário final. O sistema não deve usar uma abordagem que imite eventos de integração, como SignalR ou sistemas de sondagem do cliente. O usuário final e o proprietário da empresa precisam adotar explicitamente consistência eventual no sistema e observar que, em muitos casos, os negócios não têm nenhum problema com essa abordagem, desde que ela seja explícita. Isso é importante porque talvez os usuários esperem resultados imediatos e isso pode não acontecer com a consistência eventual.

Conforme observado anteriormente na seção Desafios e soluções para o gerenciamento de dados distribuídos, você pode usar eventos de integração para implementar as tarefas de negócios que abrangem vários microsserviços. Assim, você terá consistência eventual entre esses serviços. Uma transação eventualmente consistente é composta por uma coleção de ações distribuídas. Em cada ação, o microsserviço relacionado atualiza uma entidade de domínio e publica outro evento de integração que gera a próxima ação dentro da mesma tarefa comercial de ponta a ponta.

Um ponto importante é que você pode querer comunicar-se com vários microsserviços inscritos para o mesmo evento. Para fazer isso, você pode usar mensagens de publicação/assinatura com base em comunicação controlada por evento, conforme mostra a Figura 4-19. Esse mecanismo de publicação/assinatura não é exclusivo da arquitetura de microsserviço. Ele é semelhante à maneira como Contextos Limitados no DDD devem se comunicar ou à maneira como você propaga atualizações do banco de dados de gravação para o banco de dados de leitura no padrão de arquitetura de CQRS (Segregação de Responsabilidade de Comando e Consulta). A meta é ter consistência eventual entre várias fontes de dados em seu sistema distribuído.

Diagram showing asynchronous event-driven communications.

Figura 4-19. Comunicação de mensagem controlada por evento assíncrono

Na comunicação assíncrona controlada por evento, um microsserviço publica os eventos em um barramento de evento e vários microsserviços podem assiná-lo para serem notificados e agirem em relação a ele. Sua implementação determinará o protocolo a ser usado para comunicações controladas por eventos e baseadas em mensagem. O AMQP pode ajudar a obter uma comunicação confiável na fila.

Ao usar um barramento de evento, talvez você queira usar um nível de abstração (como uma interface de barramento de evento) com base em uma implementação relacionada em classes com o código usando a API de um agente de mensagens como RabbitMQ ou um barramento de serviço como Barramento de Serviço do Azure com Tópicos. Como alternativa, você talvez queira usar um barramento de serviço de nível superior, como NServiceBus, MassTransit ou Brighter para articular seu barramento de evento e sistema de publicação/assinatura.

Uma observação sobre tecnologias para mensagens para sistemas de produção

As tecnologias de mensagens disponíveis para implementar seu barramento do evento abstrato estão em diferentes níveis. Por exemplo, produtos, como RabbitMQ (um transporte do agente de mensagens) e o Barramento de Serviço do Azure ficam em um nível inferior a outros produtos, como NServiceBus, MassTransit ou Brighter, que podem funcionar sobre RabbitMQ e o Barramento de Serviço do Azure. Sua escolha depende de quantos recursos avançados no nível do aplicativo e escalabilidade imediata você precisa para seu aplicativo. Para implementar apenas um barramento de evento de prova de conceito para seu ambiente de desenvolvimento, como foi feito no exemplo de eShopOnContainers, pode ser suficiente uma implementação simples sobre RabbitMQ em execução em um contêiner do Docker.

No entanto, para sistemas críticos e de produção que precisam de hiperescalabilidade, talvez você queira avaliar o Barramento de Serviço do Azure. Para abstrações de alto nível e recursos que tornam o desenvolvimento de aplicativos distribuídos mais fácil, recomendamos que você avalie outros barramentos de serviço de software livre e comerciais, como NServiceBus, MassTransit e Brighter. É claro que você pode criar seus próprios recursos de barramento de serviço sobre tecnologias de nível inferior, como RabbitMQ e Docker. Mas esse trabalho pode custar muito caro para um aplicativo empresarial personalizado.

Publicação resiliente para o barramento de evento

Um desafio ao implementar uma arquitetura orientada a eventos em vários microsserviços é como atualizar atomicamente o estado no microsserviço original enquanto publica de maneira resiliente o evento de integração relacionado no barramento de evento, de alguma maneira baseado em transações. Veja a seguir algumas formas de obter essa funcionalidade, embora também haja outras abordagens.

  • Usando uma fila transacional (baseada em DTC) como MSMQ. (No entanto, essa é uma abordagem herdada.)

  • Usando mineração do log de transações.

  • Usando o padrão Event Sourcing completo.

  • Usando o Padrão de caixa de saída: uma tabela de banco de dados transacional como uma fila de mensagens que será a base de um componente do criador do evento que criará e publicará o evento.

Para obter uma descrição mais completa dos desafios nesse espaço, incluindo como mensagens com dados potencialmente incorretos podem acabar sendo publicadas, consulte Plataforma de dados para cargas de trabalho críticas no Azure: cada mensagem deve ser processada.

Tópicos adicionais a serem considerados ao usar comunicação assíncrona são idempotência de mensagem e eliminação de duplicação de mensagem. Esses tópicos são abordados na seção Implementar a comunicação baseada em eventos entre microsserviços (eventos de integração) mais adiante neste guia.

Recursos adicionais