Microsserviço 5
Na comunicação entre microsserviços, temos diversas opções de tecnologias para uso. A escolha da tecnologia adequada deve considerar o contexto e o que esperamos que ela entregue.
Expectativa
Os pontos que esperamos como resposta a partir da escolha da tecnologia são:
- facilitar a compatibilidade com versões anteriores: precisamos evitar gerar incompatibilidade com os serviços upstream. Operações simples, como adicionar um novo campo, não devem causar falhas nos clientes;
- interface explícita: deixar claros os schemas e funcionalidades expostas pelo microsserviço;
- API independente de tecnologia: evitar tecnologias de integração que determinem os conjuntos de tecnologias que poderão ser usados na implementação;
- simplicidade para os consumidores: deixar o consumidor livre quanto à escolha da tecnologia;
- ocultar detalhes internos: consumidores não devem depender da nossa implementação interna, pois isso resultará em alto acoplamento. Tecnologias que exponham nossa representação interna devem ser evitadas.
As opções de comunicação mais comuns que podemos considerar são:
- RPC (Remote Procedure Call);
- REST;
- GraphQL;
- Brokers de mensagens.
RPC
A ideia é fazer uma chamada local, mas executada em um serviço remoto. Usar essa abordagem implica adotar um protocolo de serialização, como o gRPC, que utiliza o formato Protocol Buffer. O schema explícito facilita a geração de código cliente.
Os desafios na utilização são:
- acoplamento de tecnologias: algumas soluções, como o Java RMI, estão extremamente vinculadas à plataforma específica;
- chamadas locais não são como chamadas remotas: o fato de uma chamada local não parecer remota esconde diferenças cruciais sobre o custo de execução e custo de latência. Serialização e deserialização são pontos críticos, já que as redes não são confiáveis e irão falhar;
- fragilidade: mudanças no schema fazem com que os clientes precisem gerar stubs novamente. Lançamentos sincronizados serão uma realidade. Na prática, objetos usados como parte de uma serialização binária transmitida pela rede podem ser pensados como tipos somente expansíveis.
Implementações mais modernas, como o gRPC, são excelentes, enquanto o Java RMI possui sérios problemas de fragilidade.
Evite abstrair chamadas remotas a ponto de ocultar totalmente a rede. Direcione a escolha para essa tecnologia somente se tiver controle do lado do cliente e do servidor.
REST
REST não significa HTTP, e HTTP não significa REST.
Quando pensamos em REST, pensamos em recursos. Assim, recursos apresentados ao mundo externo são totalmente desacoplados do modo como são armazenados internamente. REST é um modelo arquitetural, não uma especificação, já que pode ser implementado em diversos protocolos, sendo o mais comum o HTTP.
O ponto do REST é que, com o HTTP, já temos semântica e significados bem conhecidos sobre o funcionamento dos recursos. Além disso, o HTTP traz consigo um grande ecossistema de ferramentas e tecnologias de apoio, agregado a diversos controles de segurança que podem ser utilizados para proteção das comunicações.
Dentro do REST, temos o conceito de HATEOAS, que basicamente define como os clientes devem interagir com o servidor por meio de links, nos quais essas ligações podem resultar em mudanças de estado. Entretanto, não é tão comum a criação e utilização desse conceito pela indústria.
Um dos grandes desafios no uso de REST é o desempenho em ambientes restritos. Payloads sobre HTTP são compactos, mas não tão pequenos quanto um protocolo binário. Além disso, o overhead em cada requisição pode ser uma preocupação em ambientes que exigem baixa latência. Todavia, uma grande vantagem do REST sobre HTTP, considerando uma interface síncrona request/response, é a facilidade de utilização. Por ser um estilo amplamente conhecido, muitas pessoas têm familiaridade, além de garantir interoperabilidade com diversas tecnologias.
GraphQL
O ponto-chave do GraphQL é permitir que o serviço upstream tenha a liberdade de definir queries, o que pode evitar a necessidade de fazer várias requisições, trazendo melhora de desempenho em dispositivos limitados do lado do cliente. Uma única query pode retornar várias informações.
O desafio fica no backend, já que essa dinâmica de executar queries que mudam frequentemente gera maior carga tanto no servidor quanto no banco de dados. Outro ponto é que o GraphQL é mais apropriado para leituras, mesmo sendo capaz de lidar com escritas. Também é essencial que a API não esteja acoplada ao repositório de dados, ou seja, a resposta do serviço não deve representar exatamente a estrutura do banco.
Basicamente, seu uso é mais direcionado para dispositivos móveis, não sendo um substituto para comunicação genérica entre microsserviços.
Brokers de mensagens
Os brokers de mensagens são intermediários, também conhecidos como middlewares. A abordagem do broker é: em vez de o serviço upstream conversar diretamente com o serviço downstream, o upstream envia uma mensagem para o broker, e o downstream a recebe do broker. Para isso, podemos usar tópicos ou filas.
Com a fila, o serviço upstream envia a mensagem e o downstream a consome, em um modelo de distribuição de carga com consumidores concorrentes.
Com o tópico, vários serviços downstream estão inscritos e cada um receberá uma cópia da mensagem, podendo haver também grupos de consumidores. Essa abordagem é muito usada para colaboração baseada em eventos.
O ponto em usar broker é a garantia de entrega, já que mesmo que o downstream esteja indisponível, o broker armazena a mensagem até poder entregá-la. Para maior segurança e disponibilidade, a execução do broker deve ser realizada em cluster.
Outras características que os brokers podem oferecer são a ordenação das mensagens e transações na escrita em vários tópicos.
Formatos de Serialização
Os tipos de serialização mais comuns são formatos textuais e formatos binários, estando diretamente relacionados ao tipo de tecnologia utilizada.
O formato textual entrega fácil leitura, interoperabilidade e muita flexibilidade no envio e consumo de recursos, por exemplo, no uso de APIs REST em request/response.
O formato json tornou-se o mais comum, superando o XML. Existe também o AVRO, que utiliza o json como formato de schema.
O formato binário é uma opção quando o tamanho dos payloads e a eficiência na leitura/escrita são requisitos importantes, além de possibilitar comunicação com baixa latência.
Schemas
Os schemas podem ser de vários tipos, e geralmente a escolha do formato de serialização definirá a tecnologia de esquema utilizada.
Ter schemas explícitos para endpoints de microsserviços ajuda a representar de forma clara aquilo que o serviço expõe e aceita. Não substituem a necessidade de documentação, mas reduzem o volume necessário. Outro ponto é facilitar a identificação de incompatibilidades acidentais nos endpoints.
No contexto de incompatibilidade, podemos separar as incompatibilidades de contrato em duas categorias: estrutural e semântica.
Na estrutural, ocorre quando a estrutura do endpoint muda a ponto de o consumidor deixar de ser compatível, por exemplo, quando campos ou métodos são adicionados ou removidos.
Na semântica, a estrutura do endpoint permanece a mesma, mas o comportamento muda, de modo que as expectativas dos consumidores deixam de ser atendidas. Exemplo: um endpoint que retorna a idade do usuário em dias passa a retornar em meses. Esse tipo de incompatibilidade exige o uso de testes para identificação.
Por isso, o melhor caminho é ser o mais explícito possível quanto ao que o microsserviço expõe, e, para isso, os schemas ajudam muito. Mas, em cenários em que tanto o serviço upstream quanto o downstream são de responsabilidade da mesma equipe, pode ser que a ausência de schemas não seja um problema.
Conclusão
A escolha da tecnologia de comunicação em microsserviços envolve analisar cuidadosamente o contexto de uso, os requisitos de desempenho, a facilidade de evolução sem quebra de compatibilidade e o nível de acoplamento aceitável. RPC, REST, GraphQL e brokers de mensagens possuem pontos fortes e fracos, e nenhum deve ser considerado solução universal. Além disso, aspectos como serialização, schemas explícitos, gestão de mudanças e contratos bem definidos são essenciais para manter a interoperabilidade e a evolução contínua. Em última análise, arquiteturas de microsserviços eficazes nascem do equilíbrio entre flexibilidade, clareza de contratos e resiliência frente a mudanças.
