Skip to main content

Microsserviço 6

· 5 min read
Leandro Andrade
Leandro Andrade
Software Developer

Podemos utilizar vários microsserviços colaborando entre si para implementar um processo de negócio.

Pensando no contexto de transação de banco de dados, temos várias ações que irão acontecer, mas queremos tratar todas as ações como uma unidade. Assim, utilizamos as transações para garantir que uma ou mais mudanças de estado aconteçam com sucesso.

Para que essa garantia acontecer, o banco de dados entrega o ACID:

  • atomicidade: ou todas as operações acontecem, ou todas falham.
  • consistência: quando alterações são feitas, o banco de dados continua em um estado válido.
  • isolamento: várias transações podem ser executadas ao mesmo tempo, sem interferência entre elas.
  • durabilidade: após a conclusão de uma transação, os dados não serão perdidos caso haja uma falha no sistema.

Considerando o contexto de atomicidade, ao decompor uma operação que antes era única e agora passa a ser duas transações separadas, perdemos a garantia de atomicidade com um todo, e essa falta de atomicidade pode causar problemas significativos, principalmente se fizermos a migração de sistemas que contavam com essa propriedade.

Um algoritmo comum para implementar transação distribuída é o two phase commmit.

two phase commmit

O two phase commit é usado na tentativa de permitir fazer mudanças transacionais em sistemas distribuídos, nas quais vários processos são parte da operação como um todo. Esse algoritmo é dividido em duas fases:

  • fase de votação: um coordenador entra em contato com os workers que farão parte de transação para confirmar se a mudança de estado pode ou não ser feita. Caso todos os workers confirmem, o algoritmo passa para a próxima fase (fase de commit). Caso algum worker informe que a mudança não pode ser feita, a operação como um todo é cancelada.
  • fase de commit: neste ponto, o coordenador envia a requisição de commit para que todos os workers a executem. É importante observar que não é possível garantir que todos os commits serão realizados no mesmo instante já que a mensagem pode chegar em momentos diferentes em cada worker. Quanto mais workers, maior será a latência do sistema, maior será o período em que os recursos ficarão travados(lock distribuído).

Assim, evite usar essa abordagem, mantenha os estados em um único banco de dados e deixe a funcionalidade que administra esses estados com um único serviço.

Sagas

Mesmo com sagas, não teremos a atomicidade fornecidada pelo ACID. A atomicidade só existirá dentro de cada transação individual dos serviços que compõem a saga.

Com Sagas, é possível executar operações envolvendo vários serviços, coordenando várias mudanças de estado, porém evitando o locking, podendo ser executadas de modo independente.

O primeiro ponto que as sagas apoiam é a necessidade de modelar explicitamente nosso processo de negócio. Dividimos o processo de negócio em um conjunto de chamadas que serão feitas para diferentes serviços que colaboram entre si.

Como a saga é dividida em transações individuais executadas em diferentes serviços, temos de considerar a recuperaçao em caso de falha. Para isso, temos duas abordagens:

  • recuperação com retrocesso(backward recovery): fazer rollback de transações que já tenham feito commit.
  • recuperação com avanço(forward recovery): a partir de checkpoints, prosseguir do ponto em que a falha ocorreu e continuar o processamento. Para isso, precisamos ter informações suficientes para permitir que essas novas tentativas ocorram.

O ponto é recuperar de falhas de negócio, e não de falhas técnicas. Falhas técnicas, como um erro 500, devem ser tratados separadamente.

Como são várias transações envolvidas em uma saga, para fazer o rollback de uma transação que já houve o commit, precisamos de uma transação compensatória, um rollback semântico. Por exemplo, um email já enviado ao cliente informando que o pedido está a caminho. Não tem como desenviar o email. A transação compensatória seria enviar um outro email para o cliente informando, por exemplo, que houve um problema no pedido e que esse foi cancelado.

Mas antes de chegar no ponto de ter uma transação compensatória, vale reorganizar os passos do workflow original. Às vezes, podemos simplificar as operações de rollback simplemente ajustando o modo como o workflow se desenvolve.

Implementando Sagas

Existe dois estilos de implementação das sagas:

  • sagas orquestradas: utiliza um coordenador centralizado que controla o que acontece e quando acontece, ou seja, um serviço que chama outros serviços para executar um workflow usando request/response. A vantagem é a fácil compreensão já que a análise precisa ser realizada em somente um local. A desvantagem é que essa abordagem acaba se tornando mais acoplada, já que o serviço central, ou coordenador, precisa conhecer todos os serviços associados ao workflow. Outro ponto é que o orquestrador, ou coordenador, pode acabar absorvendo a lógico de negócio que deveria estar nos serviços.
  • sagas coreografadas: abordagem muito menos acoplada, distribui a responsabilidade do workflow entre vários serviços que colaboram entre si. Geralmente faz uso intenso de eventos entre serviços. Eventos são gerados e os microsserviços reagem a esses eventos recebidos. Não enviamos eventos diretamente para os microsserviços, apenas os geramos e os microsserviços interessados nos eventos reagem. Vários microsserviços podem reagir a eventos se usarmos um tópico.

Independente da abordagem, o uso de um id de correlação deve ser usado para possibilitar a rastreabilidade do workflow, sabe o estado atual, principalmente em sagas coreografadas.

Os modelo não são excludentes e podemos, caso necessário, combiná-los para melhor atender o processo de negócio.

Resumindo: sagas orquestradas, request/response; sagas coreografadas, event-driven.