Posted on: April 25, 2023 07:36 PM
Posted by: Renato
Views: 936
Combinando RESTful HTTP com serviços assíncronos e orientados a eventos
Muitas organizações têm vários aplicativos que estão sendo criados de forma independente como uma plataforma baseada em microsserviços. Por sua natureza, uma plataforma de microsserviço é um sistema distribuído executado em vários processos ou serviços, em vários servidores ou hosts. Construir uma plataforma bem-sucedida sobre microsserviços requer a integração desses serviços e sistemas diferentes para produzir um conjunto unificado de funcionalidades.
É ingênuo pensar que só podemos escolher um estilo de comunicação para resolver todos os problemas; se as necessidades de integração fossem sempre as mesmas, haveria apenas um estilo e todos estaríamos felizes com ele. Na realidade, um único serviço pode se comunicar usando muitos estilos diferentes, cada um visando um cenário e objetivos diferentes. Este desafio é o que me traz ao tópico desta postagem no blog: quando e por que usamos diferentes padrões de comunicação?
Tipos de comunicação
Seguindo as orientações da Microsoft sobre comunicação em uma arquitetura de microsserviços , podemos começar classificando os tipos de comunicação disponíveis em dois eixos. O primeiro eixo define a comunicação síncrona versus assíncrona. O segundo eixo define o número de receptores desta mensagem.
Você pode usar várias tecnologias e implementações diferentes para oferecer suporte à comunicação síncrona e assíncrona para um ou vários receptores. Ao colocar esses sistemas de comunicação em nossos eixos, temos uma visão geral rápida dos tipos de comunicação disponíveis e quando eles seriam usados. Como primeiro passo para conectar os serviços, primeiro familiarize-se com as opções à sua disposição. O diagrama a seguir mostra algumas tecnologias de comunicação comuns e onde elas se encaixam no espectro:
Independência de serviço
É bom conhecer esses eixos para que você tenha clareza sobre os possíveis tipos de comunicação, mas focar muito nas tecnologias individuais corre o risco de perder a floresta para as árvores. O tipo de comunicação pode não ser a preocupação mais importante ao criar microsserviços. O importante é poder integrar mantendo a independência de microsserviços. Em vez de se preocupar com qual tipo de comunicação usar, esforce-se para minimizar completamente a amplitude e a profundidade da comunicação entre os microsserviços. Quanto menos comunicações entre microsserviços, melhor. Quando você precisa se comunicar, a regra crítica a seguir é evitar o encadeamento de várias chamadas síncronas entre microsserviços. Isso não significa que você tenha que usar um protocolo específico (por exemplo, NATS ou HTTP). Significa apenas que a comunicação entre microsserviços deve ser feita propagando dados em paralelo, preferencialmente de forma assíncrona, ao invés de uma série síncrona. O Guia de Arquitetura da Microsoft para .NET classifica o encadeamento de chamadas de API síncronas como Antipadrão, conforme ilustrado na figura a seguir:
O Parceiro AKF liga para múltiplas chamadas síncronas para o Anti-Padrão das Luzes da Árvore de Natal — se uma lâmpada falhar, toda a cadeia fica escura.
Para enfrentar os desafios das chamadas de API em série, existem alguns padrões e diretrizes que podem nos ajudar.
Prefira Comunicação Assíncrona
RESTful HTTP e RPC tendem a promover o encapsulamento ao ocultar a estrutura de dados de um serviço por trás de uma API bem definida, eliminando a necessidade de uma grande estrutura de dados compartilhada entre as equipes. Infelizmente, confiar completamente em RESTful HTTP ou RPC significa que os aplicativos ainda estão fortemente acoplados - as chamadas remotas entre serviços tendem a amarrar os diferentes sistemas em um nó cada vez maior. Esses tipos de problemas geralmente surgem porque problemas que não são significativos em um único aplicativo tornam-se problemas significativos ao integrar vários aplicativos.
A comunicação assíncrona é fundamentalmente uma reação pragmática aos problemas dos sistemas distribuídos. Enviar uma mensagem não requer que ambos os sistemas estejam ativos e disponíveis ao mesmo tempo. Além disso, pensar na comunicação de forma assíncrona obriga os desenvolvedores a reconhecer que trabalhar com uma aplicação remota é mais lento e propenso a falhas, o que incentiva o design de componentes com alta coesão (muito trabalho local) e baixa adesão (trabalho seletivo remoto).
Padrões de integração empresarial
Curiosamente, a comunicação assíncrona pode acontecer com ou sem um sistema de mensagens ou barramento de mensagens, aproveitando web hooks ou sondagem longa. Por exemplo, o diagrama a seguir de Enterprise Integration Patterns descreve um barramento de mensagem típico que facilita a comunicação entre vários serviços.
Esse sistema pode ser replicado usando uma tecnologia normalmente associada à comunicação síncrona, como HTTP, registrando assinantes interessantes com uma URL de retorno de chamada e usando uma solicitação POST para publicar dados diretamente aos assinantes, ignorando a resposta. Este diagrama de Why Messaging Queues Suck mostra um exemplo de conexão de vários serviços usando webhooks.
Em ambos os casos, o atributo chave da comunicação assíncrona eficaz é que o serviço solicitante não deve se importar se recebe uma resposta. Isso nos permite chamar vários serviços em paralelo, compô-los ou orquestrá-los sem encadeá-los em uma longa série. Se o seu microsserviço precisar gerar uma ação adicional em outro microsserviço, se possível, faça-o de forma assíncrona (usando mensagens assíncronas ou eventos de integração, filas, etc.). Mas, tanto quanto possível, não invoque a ação de forma síncrona como parte da solicitação síncrona original e da operação de resposta.
Você pode usar qualquer protocolo para se comunicar e propagar dados de forma assíncrona entre microsserviços para ter consistência eventual. Não importa. A regra importante é não criar dependências síncronas entre seus microsserviços.
Casando comunicação assíncrona com uma plataforma RESTful
Mencionei no início deste artigo que é ingênuo pensar que um estilo de comunicação satisfará todas as necessidades de integração. Um grande exemplo que é parte importante da estratégia de muitas empresas: RESTful HTTP APIs. O REST é um dos protocolos de comunicação mais onipresentes e amplamente utilizados no mundo, o que o torna uma parte fundamental das iniciativas de muitas organizações.
Se preferirmos a comunicação assíncrona, mas exigirmos RESTful HTTP, como os dois podem coexistir? Minha sugestão é aproveitar o RESTful HTTP nos limites do seu sistema usando um padrão API Gateway ou Backends for Frontends. Com esses padrões, as APIs baseadas em RESTful HTTP servem como ponto de entrada e ponto de interação entre limites lógicos dentro do sistema. Essas APIs devem ser bem definidas, claramente documentadas, estáveis e adequadas para uso interno e externo.
O exemplo a seguir, de APIs You Won't Hate , mostra como os limites entre dois sistemas diferentes interagem em uma API RESTful clara e estável, enquanto os microsserviços individuais dentro de cada limite utilizam um protocolo RPC.
As coisas dentro do contexto podem tratar suas próprias APIs como “aulas particulares” em linguagens de programação, podem mudar quando quiserem, girar para cima e para baixo, excluir, evoluir, alterar, quem se importa. Ao ir para outro contexto … [deveríamos] provavelmente usar coisas como REST (com Hypermedia e JSON Schema) para ajudar esses clientes a durar mais tempo sem precisar do envolvimento do desenvolvedor para a maioria das alterações.
Um ponto chave no diagrama é que ambos os sistemas possuem serviços que representam o mesmo conceito (Cliente e Produto). Os puristas DRY entre nós podem zombar disso ou querer consertá-lo. Resista a esse desejo! Em vez disso, trate esses pontos comuns como os pontos de integração entre dois sistemas. Cada sistema é livre para modificar sua visão do que é um Cliente ou Produto sem afetar o outro sistema, e eles podem sincronizar essa visão conforme necessário por meio de suas respectivas APIs.
Com este modelo, a comunicação de serviço interna ao seu sistema pode usar uma escolha flexível de padrões e protocolos, mas quando você está começando a expor APIs para desenvolvedores com os quais você não está em comunicação próxima (e que também podem ter outras prioridades), o adicional nível de abstração de uma API baseada em HTTP torna-se muito mais útil.
O Lugar das Filas é no Consumidor
Agora, e as filas? As filas têm muitos usos, mas aproveitá-los como um barramento de mensagem global pode não ser um deles. Ao integrar com serviços, você pode tratar seu trabalho como entrega de informações. É trabalho do serviço consumi-lo. As filas são um detalhe do consumidor, conforme representado por este diagrama de Why Messaging Queues Suck mostra um exemplo de conexão de vários serviços usando webhooks.
Nesse caso, uma fila atua como um buffer entre a API que você expõe e o serviço que processa os dados. A fila ajuda a suavizar cargas pesadas intermitentes que podem causar falha no serviço ou tempo limite da tarefa. Isso pode ajudar a minimizar o impacto dos picos de demanda na disponibilidade e na capacidade de resposta da API e do serviço. A fila separa as tarefas do serviço e o serviço pode manipular as mensagens em seu próprio ritmo, independentemente do volume de solicitações de tarefas simultâneas.
Aproveitar uma fila para ajudar no consumo de mensagens é um ótimo exemplo de como aproveitar o design assíncrono, que também ajuda a dimensionar um sistema para processar várias mensagens simultaneamente, otimizando a taxa de transferência, melhorando a escalabilidade e a disponibilidade e equilibrando a carga de trabalho. A Microsoft chama esse padrão de nivelamento de carga baseado em fila
Os eventos vão em um fluxo de eventos
Em um sistema orientado a eventos, os eventos são entregues quase em tempo real, para que os consumidores possam responder imediatamente aos eventos à medida que ocorrem. Os produtores são dissociados dos consumidores - um produtor não sabe quais consumidores estão ouvindo. Os consumidores também são desacoplados uns dos outros e cada consumidor vê todos os eventos. Esse cenário difere um pouco do enfileiramento, em que os consumidores extraem mensagens de uma fila e uma mensagem é processada por um único consumidor.
Os sistemas orientados a eventos são mais bem usados em alguns cenários diferentes:
- Você tem vários consumidores e cada um deve processar o mesmo conjunto de eventos.
- Você precisa de processamento em tempo real com intervalo de tempo mínimo.
- Você tem um processamento de eventos complexo, como correspondência de padrões ou agregação em janelas de tempo.
- Você está construindo um aplicativo originado de eventos em que os eventos, em vez dos dados, são a fonte da verdade para o estado do aplicativo.
A desvantagem dos sistemas orientados a eventos é que eles geralmente são mais complexos do que aplicativos CRUD simples; você precisa decidir se a complexidade extra vale o benefício.
Os sistemas orientados a eventos podem ser construídos sobre um modelo de publicação-assinatura ou um modelo de fluxo de eventos:
- Mensagens de publicação e assinatura com entrega garantida: a infraestrutura de mensagens acompanha as assinaturas. Quando um evento é publicado, ele envia o evento para cada assinante. Depois que um evento é recebido, ele não pode ser repetido e novos assinantes não veem o evento.
- Streaming de eventos: os eventos são gravados em um log. Os eventos são estritamente ordenados (dentro de uma partição) e duráveis. Os clientes não se inscrevem no fluxo, em vez disso, um cliente pode ler qualquer parte do fluxo. O cliente é responsável por avançar sua posição no fluxo. Isso significa que um cliente pode ingressar a qualquer momento e reproduzir eventos.
Para consumidores de eventos, a integração é feita ao se inscrever no tópico pub/sub ou fluxo de eventos de interesse, levando a outro padrão de comunicação assíncrona: ouvir um fluxo de eventos.
Juntando tudo — um breve exemplo
Ufa! Se você estiver acompanhando, abordamos a comunicação síncrona usando REST e RPC e a comunicação assíncrona usando um barramento de mensagens, enfileiramento e fluxos de eventos. Também falamos sobre como, na prática, você precisa de mais de um desses tipos de comunicação para construir um serviço completo. Então, como você escolhe? Já cobrimos algumas diretrizes, mas é útil ver um exemplo prático de como você pode estruturar seu aplicativo para aproveitar as diferentes opções de comunicação. Este exemplo começa como você deve desenvolver seu aplicativo como um único serviço e, posteriormente, criar vários serviços à medida que a complexidade aumenta.
Um Serviço RESTful Simples
O ponto de partida para pensar em comunicação é começar com um único serviço. Nesse caso, o melhor caminho a seguir é criar uma API RESTful simples que corresponda aos nossos padrões externos de API e usá-la como API para seu serviço. A escolha de uma API RESTful, em vez de uma estrutura RPC, nos permite acelerar a criação de APIs que nossos clientes terceirizados podem aproveitar. Opcionalmente, as alterações no estado do aplicativo local podem ser enviadas para um fluxo de eventos para permitir que outras equipes atualizem seus sistemas de forma assíncrona.
Vários serviços
À medida que seu serviço cresce, você pode optar por dividir seu serviço RESTful em vários microsserviços. Parabéns! Agora você tem um sistema distribuído! Isso significa que você deve se preparar para um conjunto adicional de desafios e deve começar a favorecer a comunicação assíncrona entre os serviços. Tornar-se totalmente assíncrono limita nossa capacidade de liberar APIs de terceiros, portanto, a abordagem correta aqui é manter sua API RESTful que você criou em seu único serviço e usá-la como front-end no limite de seu sistema. No que diz respeito aos desenvolvedores fora do seu esquadrão, a única API para o seu sistema é o front-end RESTful e o fluxo de eventos - exatamente como se fosse um único serviço.
Dentro dos limites do seu sistema, você pode optar por organizar seus microsserviços de maneiras que façam sentido para sua equipe, incluindo o aproveitamento da comunicação assíncrona por meio de um intermediário de mensagem ou uma estrutura RPC como Frugal. A principal coisa a ter em mente é manter uma API de front-end estável em seu sistema - esforce-se para evitar o vazamento de detalhes internos o máximo possível.
O fluxo de eventos que você possui ainda oferece a outros desenvolvedores uma maneira de receber atualizações de forma assíncrona para qualquer estado do aplicativo.
Múltiplos consumidores
Embora tenhamos uma boa API RESTful, é improvável que a mesma API seja útil para o navegador, outros serviços de back-end, clientes terceirizados e um aplicativo móvel ao mesmo tempo. Se você se deparar com vários usuários com diferentes necessidades de integração, poderá adicionar front-ends de API adicionais que mantêm o limite entre seu sistema e consumidores externos usando o padrão Backend for Front-ends.
Balanceamento de carga com uma fila
À medida que seu serviço continua a crescer em utilidade e usuários, você pode acabar precisando equilibrar a carga usando uma fila. Lembrando que as filas pertencem ao consumidor, você pode atualizar seu sistema para incluir uma fila para alavancar o nivelamento de carga baseado em fila ou aumentar o throughput adicionando consumidores de fila adicionais.
Esse tipo de alteração agora torna sua API RESTful assíncrona, o que pode exigir que você e seus consumidores façam alguns ajustes em como eles interagem com seu serviço.
Como criar uma plataforma combinável
Eu enquadrei nosso pequeno exemplo no contexto de uma equipe construindo um conjunto de serviços para resolver seus problemas específicos usando um front-end de API RESTful acoplado a um fluxo de eventos assíncrono como interface para seu sistema. Essa interface é independente do número e configuração de microsserviços individuais dentro do limite do sistema. No que diz respeito aos consumidores externos, o sistema é uma API RESTful e uma série de eventos. Internamente à equipe, a escolha de usar uma estrutura RPC, uma fila ou um barramento de mensagem assíncrono deve ser tratada principalmente como um detalhe de implementação. A orientação sugerida aqui é favorecer a comunicação assíncrona, onde não for possível favorecer APIs RESTful para que estejamos melhor posicionados para atender às necessidades de clientes terceirizados,
A imagem a seguir mostra como dois sistemas podem ser compostos aproveitando seus front-ends de API e eventos para integração entre eles.
Com cada equipe endereçando as APIs para seu sistema dessa maneira, podemos começar a construir uma plataforma composta de APIs RESTful e fluxos de eventos que um bom conjunto de princípios de arquitetura:
- Nenhum ponto único de falha. Filas e/ou tópicos são localizados em um contexto limitado em vez de usar um barramento de mensagens global. Isso também permite uma arquitetura escalável usando escala z sem ter que replicar todo o sistema.
- Isolar falhas. Ao separar detalhes privados em seu limite de sistema local, quaisquer falhas são localizadas no contexto limitado, limitando o raio de explosão para falha.
- Projeto assíncrono. Esse design oferece suporte a um design assíncrono e orientado a eventos.
- Apátrida. API Frontends (também chamados de Backends for Frontends ou BFFs) são naturalmente sem estado e escaláveis usando. Os back-ends para front-ends podem ser combinados com trabalhadores escalonáveis e sem estado no padrão de nivelamento de carga baseado em fila ou consumidor competitivo.
- Escale para fora, não para cima. BFFs e nivelamento de carga baseado em fila são naturalmente escaláveis. Dividir o tráfego RPC de um NATS global permite que nossa organização expanda de forma mais eficaz do que depender da expansão do uso de NATS indefinidamente.
- Pense externamente primeiro. BFFs em cada borda de um Bounded Context suportam APIs de terceiros. As APIs que você expõe a outras pessoas são projetadas intencionalmente e oferecem suporte a padrões abertos.'
Fonte: - https://sookocheff.com/post/api/marrying-restful-http-with-asynchronous-design/
Donate to Site
Renato
Developer