Introdução
Como você sabe, a transição de uma arquitetura de monólito para uma arquitetura de microsserviço causa uma série de dificuldades associadas tanto à parte técnica do projeto quanto ao fator humano. Um dos desafios técnicos mais difíceis é garantir a consistência em um sistema distribuído.
Consistência
Um ponto bastante sutil é que a consistência no contexto de sistemas distribuídos é diferente da consistência no contexto de bancos de dados. Além disso, por consistência, queremos dizer exatamente o primeiro: uma operação incompleta (errônea) não introduz nenhum efeito e não altera os dados; com acesso simultâneo aos dados, todas as operações são consideradas atômicas (você não pode ver o resultado intermediário da operação) se os dados tiverem várias cópias (replicação) , então a sequência de aplicação de operações em todas as cópias é a mesma. Ou seja, queremos receber uma transação ACID, mas apenas distribuída.
A causa do problema
Por que é difícil manter a consistência em uma arquitetura de microsserviço? O fato é que esse estilo de arquitetura frequentemente envolve o uso do banco de dados por padrão de serviço. Lembrarei que este padrão consiste no fato de que cada microsserviço possui sua própria base ou bases independentes (bases, pois além da fonte primária de dados, por exemplo, pode ser utilizado um cache). Esta abordagem permite, por um lado, não adicionar links de formato de dados implícitos entre microsserviços (microsserviços interagem apenas explicitamente por meio da API), por outro lado, aproveitar ao máximo essa vantagem da arquitetura de microsserviço como tecnologia agnóstica (podemos escolher a tecnologia de armazenamento de dados adequada para a carga específica no microsserviço ) Mas com tudo isso, perdemos a garantia da consistência dos dados. Julgue por si mesmoo monólito se comunicava com um grande banco de dados, que fornecia a capacidade de fornecer transações ACID. Agora, existem muitos bancos de dados e, em vez de uma grande transação ACID, temos muitas pequenas transações ACID. Nossa tarefa será combinar todas essas transações em umdistribuído .
Consistência otimista
A primeira coisa que vem à mente é o conceito de consistência otimista: confirmamos quantas transações quisermos para quantos mecanismos de armazenamento forem necessários. Ao mesmo tempo, esperamos que tudo dê certo, e se tudo estiver ruim, então dizemos que tudo ficará bem no final. Se tudo der errado, então dizemos: "Sim, isso acontece, mas com uma probabilidade extremamente baixa."
Brincadeiras à parte, negligenciar a consistência quando não é crítica para os negócios é uma boa ideia, especialmente considerando quanto esforço nos custará mantê-la (o que espero que você veja mais tarde).
Opções de consistência
Se a consistência for crítica para o negócio, existem várias maneiras de tentar alcançá-la. Se estivermos falando sobre uma situação em que os dados são atualizados por um serviço (por exemplo, ocorre a replicação do banco de dados), então algoritmos de consistência padrão como Paxos ou Raft podem ser aplicados. Essas transações são chamadas de homogêneas . Se os dados são atualizados por vários serviços (ou seja, ocorre uma transação heterogênea ), então como começa a dificuldade, da qual falamos acima.
Por um lado, ainda podemos contornar a necessidade de fornecer uma transação distribuída, buscando uma arquitetura baseada em serviços (combinamos serviços de forma que a transação seja homogênea). Essa solução não é muito canônica do ponto de vista dos princípios da arquitetura de microsserviços, mas é tecnicamente muito mais simples, por isso é frequentemente usada na prática. Por outro lado, podemos sair dos microsserviços canônicos, mas ao mesmo tempo aplicar um dos mecanismos para garantir transações distribuídas: um commit de duas fases ou uma saga. Este artigo explorará a primeira opção e discutirá a segunda na próxima vez.
Compromisso de duas fases
O mecanismo é extremamente simples: existe algum gerenciador de transações que realmente orquestra a transação. No primeiro estágio (preparar), o gerenciador de transações emite o comando apropriado para gerenciadores de recursos, de acordo com o qual eles gravam dados em seus logs que serão confirmados. Após receber a confirmação de todos os gerenciadores de recursos sobre a conclusão bem-sucedida do primeiro estágio, o gerenciador de transações inicia o segundo estágio e emite o próximo comando (commit), de acordo com o qual os gerenciadores de recursos aplicam as alterações aceitas anteriormente.
Apesar de sua aparente simplicidade, essa abordagem tem uma série de desvantagens. Primeiro, se pelo menos um gerenciador de recursos falhar na segunda fase, toda a transação deverá ser revertida. Assim, um dos princípios da arquitetura de microsserviço é violado - a resistência a falhas (quando chegamos a um sistema distribuído, imediatamente assumimos que falha nele é a norma e não uma situação excepcional). Além disso, se houver muitas falhas (e haverá muitas delas), o processo de cancelamento de transações precisará ser automatizado (incluindo transações de gravação que revertem transações). Em segundo lugar, o próprio gerenciador de transações é um ponto único de falha. Ele deve ser capaz de emitir id-shniks transacionais para transações. Terceiro, uma vez que comandos especiais são dados ao repositório, é lógico supor que o repositório deve ser capaz de fazer isso,ou seja, está em conformidade com o padrão XA, e nem todas as tecnologias modernas estão em conformidade com ele (corretores como Kafka, RabbitMQ e soluções NoSQL como MongoDB e Cassandra não suportam commits de duas fases).
A conclusão que se sugere a partir de todos esses fatores foi belamente articulada por Chris Richardson: "2PC não é uma opção" (o commit de duas fases não é uma opção).
Resultado
Descobrimos por que as transações distribuídas são o principal problema técnico de uma arquitetura de microsserviço e falamos sobre as várias opções para resolver esse problema, discutindo em detalhes o mecanismo de confirmação de duas fases.
Convido a todos a se inscreverem no meu webinar sobre o curso , no qual contarei em detalhes o formato do treinamento e apresentarei a todos o programa de treinamento.