Comunicação assíncrona baseada em mensagens
Gorjeta
Este conteúdo é um trecho do eBook, .NET Microservices Architecture for Containerized .NET Applications, disponível no .NET Docs ou como um PDF para download gratuito que pode ser lido offline.
As mensagens assíncronas e a comunicação orientada a eventos são essenciais ao propagar alterações em vários microsserviços e seus modelos de domínio relacionados. Como mencionado anteriormente na discussão microsserviços e contextos limitados (BCs), modelos (usuário, cliente, produto, conta, etc.) podem significar coisas diferentes para diferentes microsserviços ou BCs. Isso significa que, quando as mudanças ocorrem, você precisa de alguma maneira de conciliar as mudanças entre os diferentes modelos. Uma solução é a consistência eventual e a comunicação orientada a eventos com base em mensagens assíncronas.
Ao usar mensagens, os processos se comunicam trocando mensagens de forma assíncrona. Um cliente faz um comando ou uma solicitação para um serviço enviando-lhe uma mensagem. Se o serviço precisar responder, ele envia uma mensagem diferente de volta para o cliente. Como é uma comunicação baseada em mensagem, o cliente assume que a resposta não será recebida imediatamente e que pode não haver resposta.
Uma mensagem é composta por um cabeçalho (metadados, como identificação ou informações de segurança) e um corpo. As mensagens geralmente são enviadas através de protocolos assíncronos como AMQP.
A infraestrutura preferida para esse tipo de comunicação na comunidade de microsserviços é um agente de mensagens leve, que é diferente dos grandes corretores e orquestradores usados em SOA. Em um agente de mensagens leve, a infraestrutura normalmente é "", agindo apenas como um agente de mensagens, com implementações simples, como RabbitMQ , ou um barramento de serviço escalável na nuvem, como o Azure Service Bus. Nesse cenário, a maior parte do pensamento "inteligente" ainda vive nos endpoints que estão produzindo e consumindo mensagens, ou seja, nos microsserviços.
Outra regra que você deve tentar seguir, tanto quanto possível, é usar apenas mensagens assíncronas entre os serviços internos e usar comunicação síncrona (como HTTP) somente dos aplicativos cliente para os serviços front-end (API Gateways mais o primeiro nível de microsserviços).
Existem dois tipos de comunicação assíncrona de mensagens: comunicação baseada em mensagem de recetor único e comunicação baseada em mensagem de vários recetores. As seções a seguir fornecem detalhes sobre eles.
Comunicação baseada em mensagens de recetor único
A comunicação assíncrona baseada em mensagem com um único recetor significa que há comunicação ponto-a-ponto que entrega uma mensagem exatamente a um dos consumidores que está lendo do canal e que a 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 pode ser reenviada várias vezes. Devido a falhas de rede ou outras, o cliente tem que ser capaz de tentar novamente enviar mensagens, e o servidor tem que implementar uma operação para ser idempotente , a fim de processar uma determinada mensagem apenas uma vez.
A comunicação baseada em mensagem de recetor único é especialmente adequada para enviar comandos assíncronos de um microsserviço para outro, como mostra a Figura 4-18, que ilustra essa abordagem.
Depois de começar a enviar comunicação baseada em mensagem (com comandos ou eventos), você deve evitar misturar comunicação baseada em mensagem com comunicação HTTP síncrona.
Figura 4-18. Um único microsserviço recebendo uma mensagem assíncrona
Quando os comandos vê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 com vários recetores
Como uma abordagem mais flexível, você também pode querer usar um mecanismo de publicação/assinatura para que sua comunicação do remetente esteja disponível para microsserviços de assinantes adicionais ou para aplicativos externos. Assim, ajuda-o a seguir o princípio aberto/fechado no serviço de envio. Dessa forma, assinantes adicionais podem ser adicionados no futuro sem a necessidade de modificar o serviço de remetente.
Ao usar uma comunicação de publicação/assinatura, você pode estar usando uma interface de barramento de eventos para publicar eventos para qualquer assinante.
Comunicação assíncrona orientada a eventos
Ao usar a comunicação assíncrona orientada a eventos, um microsserviço publica um evento de integração quando algo acontece dentro de seu domínio e outro microsserviço precisa estar ciente disso, como uma alteração de preço em um microsserviço de catálogo de produtos. Microsserviços adicionais assinam os eventos para que possam recebê-los de forma assíncrona. Quando isso acontece, os recetores podem atualizar suas próprias entidades de domínio, o que pode fazer com que mais eventos de integração sejam publicados. Este sistema de publicação/subscrição é realizado utilizando uma implementação de um barramento de eventos. O barramento de eventos pode ser projetado como uma abstração ou interface, com a API necessária para assinar ou cancelar a assinatura de eventos e publicar eventos. O barramento de eventos também pode ter uma ou mais implementações baseadas em qualquer interprocesso e agente de mensagens, como uma fila de mensagens ou barramento de serviço que suporta comunicação assíncrona e um modelo de publicação/assinatura.
Se um sistema usa consistência eventual impulsionada por eventos de integração, é recomendável que essa abordagem seja deixada 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 do negócio têm que abraçar explicitamente uma eventual consistência no sistema e perceber que, em muitos casos, a empresa não tem nenhum problema com essa abordagem, desde que seja explícita. Essa abordagem é importante porque os usuários podem esperar ver alguns resultados imediatamente e esse aspeto pode não acontecer com consistência eventual.
Conforme observado anteriormente na seção Desafios e soluções para gerenciamento de dados distribuídos, você pode usar eventos de integração para implementar tarefas de negócios que abrangem vários microsserviços. Assim, você terá uma eventual consistência 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 de negócios de ponta a ponta.
Um ponto importante é que você pode querer se comunicar com vários microsserviços que estão inscritos no mesmo evento. Para fazer isso, você pode usar mensagens de publicação/assinatura com base na comunicação orientada a eventos, como mostra a Figura 4-19. Este mecanismo de publicação/subscrição não é exclusivo da arquitetura de microsserviços. É semelhante à maneira como os Contextos Delimitados 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 CQRS (Command and Query Responsibility Segregation). O objetivo é ter uma eventual consistência entre várias fontes de dados em todo o seu sistema distribuído.
Figura 4-19. Comunicação assíncrona de mensagens orientada a eventos
Na comunicação assíncrona orientada a eventos, um microsserviço publica eventos em um barramento de eventos e muitos microsserviços podem se inscrever nele, para serem notificados e agirem de acordo com ele. Sua implementação determinará qual protocolo usar para comunicações orientadas a eventos e baseadas em mensagens. O AMQP pode ajudar a obter uma comunicação confiável em fila.
A quantidade de dados a serem compartilhados nesses eventos é outra consideração importante, seja apenas um identificador ou também incluindo vários elementos de dados corporativos. Essas considerações são discutidas nesta postagem do blog sobre eventos de integração fina vs gordura.
Ao usar um barramento de eventos, convém usar um nível de abstração (como uma interface de barramento de eventos) com base em uma implementação relacionada em classes com código usando a API de um agente de mensagens como RabbitMQ ou um barramento de serviço como o Azure Service Bus with Topics. Como alternativa, você pode querer 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 nota sobre tecnologias de mensagens para sistemas de produção
As tecnologias de mensagens disponíveis para implementar seu barramento de eventos abstrato estão em diferentes níveis. Por exemplo, produtos como o RabbitMQ (um transporte de agente de mensagens) e o Azure Service Bus ficam em um nível mais baixo do que outros produtos como NServiceBus, MassTransit ou Brighter, que podem funcionar sobre o RabbitMQ e o Azure Service Bus. Sua escolha depende de quantos recursos avançados no nível do aplicativo e escalabilidade pronta para uso você precisa para seu aplicativo. Para implementar apenas um barramento de eventos de prova de conceito para seu ambiente de desenvolvimento, como foi feito no exemplo eShopOnContainers, uma implementação simples sobre o RabbitMQ em execução em um contêiner do Docker pode ser suficiente.
No entanto, para sistemas de missão crítica e de produção que precisam de hiperescalabilidade, convém avaliar o Barramento de Serviço do Azure. Para abstrações de alto nível e recursos que facilitam o desenvolvimento de aplicativos distribuídos, recomendamos que você avalie outros barramentos de serviço comerciais e de código aberto, como NServiceBus, MassTransit e Brighter. Claro, você pode criar seus próprios recursos de barramento de serviço em cima de tecnologias de nível inferior, como RabbitMQ e Docker. Mas esse trabalho de encanamento pode custar muito caro para um aplicativo corporativo personalizado.
Publicação resiliente no barramento de eventos
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 forma resiliente seu evento de integração relacionado no barramento de eventos, de alguma forma com base em transações. A seguir estão algumas maneiras de realizar essa funcionalidade, embora também possa haver abordagens adicionais.
Usando uma fila transacional (baseada em DTC) como MSMQ. (No entanto, esta é uma abordagem legada.)
Usando a mineração de log de transações.
Usando o padrão completo de Event Sourcing .
Usando o padrão Caixa de Saída: uma tabela de banco de dados transacional como uma fila de mensagens que será a base para um componente criador de eventos que criaria o evento e o publicaria.
Para obter uma descrição mais completa dos desafios neste espaço, incluindo como mensagens com dados potencialmente incorretos podem acabar sendo publicadas, consulte Plataforma de dados para cargas de trabalho de missão crítica no Azure: todas as mensagens devem ser processadas.
Outros tópicos a serem considerados ao usar a comunicação assíncrona são a idempotência da mensagem e a desduplicação da mensagem. Esses tópicos são abordados na seção Implementando a comunicação baseada em eventos entre microsserviços (eventos de integração) mais adiante neste guia.
Recursos adicionais
Mensagens orientadas a eventos
https://patterns.arcitura.com/soa-patterns/design_patterns/event_driven_messagingPublicar/Subscrever Canal
https://www.enterpriseintegrationpatterns.com/patterns/messaging/PublishSubscribeChannel.htmlUdi Dahan. CQRS clarificado
https://udidahan.com/2009/12/09/clarified-cqrs/Segregação de responsabilidade de comando e consulta (CQRS)
https://learn.microsoft.com/azure/architecture/patterns/cqrsComunicação entre contextos limitados
https://learn.microsoft.com/previous-versions/msp-n-p/jj591572(v=pandp.10)Eventual consistência
https://en.wikipedia.org/wiki/Eventual_consistencyJimmy Bogard. Refactoring Towards Resilience: Assessing Coupling
https://jimmybogard.com/refactoring-towards-resilience-evaluating-coupling/