Skip to main content

Microsserviço 4

· 5 min read
Leandro Andrade
Leandro Andrade
Software Developer

Falando sobre comunicação, chamadas entre diferentes sistemas por meio da rede e chamadas dentro de um único processo são totalmente diferentes. É relativamente fácil pensar em alterar uma chamada que um objeto faz a outro objeto para uma chamda para outro serviço. A questão é que essa mudança traz consigo muitos problemas.

O primeiro deles é o desempenho. Quando fazemos chamadas internas ao processo, o compilador e o runtime reliza 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, o overhead e a latência em uma chamada entre processos é significativamente menor comparada a uma chamada entre serviços. Além disso, coisas como serialização e deserialização de objetos, tamanho da estrutura de dados, etc, serão algo que inevitavelmente existirão na comunicação entre serviços. O volume dos dados transmitidos deverá ser otimizado e reduzido.

Outro ponto de atenção é a mudança de interfaces. Realizar mudanças interna ao processo é um processo atômico, tudo está em um único processo. Rollout de uma nova feature se torna algo simples. Agora, tratando-se de serviços, devemos pensar nos consumidores dos serviços para que a mudança não gere quebra de compatibilidade, caso a implantação gere incompatibilidade, deve ser realizada sincronizada com os consumidores.

O tratamento de erros dentro de um processo é algo deterministico e direto. Já entre serviços, estamos sujeitos a diversos tipos de erros, principalmente relacionados à falha de comunicação entre o serviço upstream e o serviço downstream (8 falácias da computação distribuída). Por isso, é extremamente necessário que exista uma semântica variada de erros devolvidos para que, clientes que executam a ação, possam realizar ações 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 conjuntos de ideias e opções acompanharão a escolha.

Itens como comunicação confiável, latência aceitável e volumetria de comunicação serão determinantes na escolha da tecnologia. Claro que as limitações e requisitos do domínio do problema também exercerão um papel importante na escolha. Além disso, uma arquitetura de microsserviços pode sim conter uma mistura de estilos.

Os estilos de comunicação são:

  • síncrono bloqueante: o serviço upstream realizada um chamada para o serviço downstream, ficando à espera de uma resposta. Exemplos são requisições HTTP ou mesmo executar uma query no banco de dados. A vantagem é a simplicidade e familiaridade. O ponto negativo é o acoplamento temporário que é gerado, ações compensatória que precisarão ser realizadas em caso de erro, além de lentidão no serviço downstream gera lentidão no serviço upstream. Além disso, grandes cadeias de chamadas devem ser evitadas.
  • assíncrono não bloqueante: o serviço upstream realizada um chamada para o serviço downstream mas não espera por uma resposta. Assim, caso o microsserviço não esteja disponível naquele momento, não necessáriamente é um problema. O ponto negativo é o nível de complexidade e variedade de opções. O melhor cenário de uso são aqueles em que temos execuções que terão uma longa duração ou longas cadeias de chamadas.
  • dados em comum: o serviço upstream insere dados em uma área de amazenagem de dados e o serviço downstream os consome. O local pode ser em um banco de dados (o que pode ser problemático) ou filesystem. A vantagem é no cenário de envio de uma grande quantidade de volume de dados ou quando houver a necessidade de interoperabilidade entre serviços com restrição de tecnologia (sistemas mais antigos como mainframes). A desvantagem é que o serviço downstream precisará ficar realizado polling na área de armazenagem, o que pode ser um problema caso o requisito principal seja baixa latência.
  • requisição-resposta: assim como o síncrono bloqueante, o serviço upstream realizada um chamada para o serviço downstream, e espera receber uma resposta. Neste caso, temos que lidar com algum timeout para evitar aguardar por uma resposta que jamais será retornada. Às vezes, não precisamos que todo um processamento seja realizado, apenas precisamos garantir que algo seja feito. Também podemos ter a abordagem síncrono não bloqueante, quando a requisição é enviada como uma mensagem para um broker de mensagem e posteriormente consumida e processada pelo serviço downstream.
  • orientada a eventos: o serviço upstream gera um evento que poderá ou não ser recebido por serviços downstream. O serviço upstream não tem ideia da intenção dos serviços downstream. Neste cenário, os brokers de mensagem como o RabbitMQ são muito utilizados. Em uma mensagem, faz-se necessário termos um id de correlação e os dados que serão necessários para processamento da mensagem, para que assim evitemos um acoplamento de domínio com chamadas HTTP entre serviços, claro que isso não é um problema, mas é sempre bom evitar. Pensemos nos dados que serão colocados na mensagem como aqueles que poderiam ser compartilhados por meio de uma API, tendo atenção para não ter uma mensagem com uma grande quantidade de dados.

Assim, existem diversos tipos de comunicação e a escolha dependerá dos requisitos do domínio do problema. Não existe opção certa, mas sim aquela que melhor atende às necessidades.