Microsserviço 4
Falando sobre comunicação, chamadas entre diferentes sistemas pela rede e chamadas dentro de um único processo são totalmente diferentes. É relativamente fácil pensar em alterar a chamada que um objeto faz a outro objeto para uma chamada que um serviço faz para outro serviço. A questão é que essa mudança traz consigo muitos desafios.
Desafios
O primeiro deles é sobre desempenho. Quando fazemos chamadas internas ao processo, o compilador e o runtime realizam diversas otimizações que podem reduzir o impacto da chamada, o que não é possível, no mesmo nível de precisão, em uma chamada entre serviços. Além disso, overhead e latência em uma chamada entre processos são significativamente menores se comparados a uma chamada entre serviços.
Outro ponto é a serialização e deserialização de objetos: o tamanho da estrutura de dados inevitavelmente existirá na comunicação entre serviços, e o volume transmitido deverá ser otimizado e reduzido.
Posteriormente, outra atenção necessária são as mudanças de interface. Realizar alterações internas ao processo é um procedimento atômico, pois tudo está em um único processo. O rollout de uma nova feature se torna simples. Todavia, em se tratando de serviços, devemos pensar nos consumidores para que a mudança não gere quebra de compatibilidade. Em caso de incompatibilidade, deve ser feita a sincronia com os consumidores.
Agora, considerando os erros, o tratamento dentro de um processo é algo determinístico e direto. Já entre serviços, estamos sujeitos a diversos tipos de falhas, principalmente de comunicação entre o serviço upstream e o serviço downstream (8 falácias da computação distribuída). Por isso, é necessário que exista uma semântica variada de erros devolvidos, para que os clientes que executam a ação possam realizar medidas compensatórias.
Comunicação
Antes de escolher a tecnologia que será utilizada, é fundamental entender os tipos de comunicação existentes, já que, ao adotar uma tecnologia específica, um conjunto de ideias e opções acompanhará a escolha.
Itens como comunicação confiável, latência aceitável e volumetria de comunicação serão determinantes na definição da tecnologia. As limitações e requisitos do domínio do problema também exercerão um papel importante. Além disso, uma arquitetura de microsserviços pode conter uma mistura de estilos.
Os estilos de comunicação são:
- síncrono bloqueante: o serviço upstream realiza uma chamada para o serviço downstream, ficando à espera de uma resposta. Exemplos: requisições HTTP ou execução de uma query em banco de dados. A vantagem é a simplicidade e familiaridade. O ponto negativo é o acoplamento temporário gerado, ações compensatórias necessárias em caso de erro e o fato de que a lentidão do serviço downstream gera lentidão no upstream. Grandes cadeias de chamadas devem ser evitadas.
- assíncrono não bloqueante: o serviço upstream realiza uma chamada para o serviço downstream mas não espera por resposta. Assim, caso o downstream não esteja disponível, não é necessariamente um problema. O ponto negativo é a maior complexidade e variedade de opções. O melhor cenário de uso é quando temos execuções de longa duração ou longas cadeias de chamadas.
- dados em comum: o serviço upstream insere dados em uma área de armazenamento, e o serviço downstream os consome. Pode ser um banco de dados ou filesystem. A vantagem está em cenários de grande volume de dados ou interoperabilidade com sistemas legados (como mainframes). O ponto negativo é que o serviço downstream precisa realizar polling no armazenamento, o que pode ser um problema quando o requisito principal é baixa latência.
- request-response: assim como no síncrono bloqueante, o serviço upstream realiza uma chamada para o downstream e espera uma resposta. Neste caso, deve haver um timeout para evitar espera indefinida. Às vezes não precisamos de todo o processamento, apenas de uma confirmação. Também podemos ter a abordagem síncrono não bloqueante, quando a requisição é enviada como mensagem para um broker e posteriormente processada pelo downstream.
- orientada a eventos: o serviço upstream gera um evento que poderá ou não ser recebido por serviços downstream. O upstream não tem conhecimento da intenção dos downstream. Neste cenário, brokers como o RabbitMQ são muito utilizados. Em uma mensagem, é essencial termos um id de correlação e os dados necessários ao processamento, para evitar acoplamento de domínio via chamadas HTTP. A ideia é que os dados incluídos na mensagem sejam equivalentes aos que poderiam ser expostos em uma API, sem excessos.
Conclusão
A comunicação em microsserviços amplia a flexibilidade e a escalabilidade dos sistemas, mas traz consigo desafios de desempenho, consistência, compatibilidade e tratamento de falhas. Cada estilo de comunicação possui vantagens e limitações, e a escolha deve considerar o domínio do problema, os requisitos de latência, volume de dados e confiabilidade esperada. Assim, combinar diferentes estilos de comunicação, aplicando-os de forma pragmática para garantir resiliência, desacoplamento e evolução contínua dos serviços tende a criar arquiteturas mais eficazes.
