O livro "Microsserviços. Padrões de desenvolvimento e refatoração »

imagem Oi, habrozhiteli! Se, durante muito tempo, parece que todo o desenvolvimento e implantação da sua empresa diminuiu ao máximo - vá para a arquitetura de microsserviços. Ele fornece desenvolvimento, entrega e implantação contínuos de aplicativos de qualquer complexidade.

O livro, destinado a desenvolvedores e arquitetos de grandes empresas, conta como projetar e escrever aplicativos no espírito da arquitetura de microsserviços. Também descreve como refatorar uma aplicação grande - e o monólito se transforma em um conjunto de microsserviços.

Oferecemos a você se familiarizar com a passagem "Gerenciamento de transações na arquitetura de microsserviços"

Quase qualquer solicitação processada por um aplicativo industrial é executada como parte de uma transação de banco de dados. Os desenvolvedores de tais aplicativos usam estruturas e bibliotecas que simplificam o trabalho com transações. Algumas ferramentas fornecem uma API imperativa para iniciar, confirmar e reverter manualmente as transações. E estruturas como o Spring têm um mecanismo declarativo. O Spring suporta a anotação @Transactional, que invoca automaticamente um método dentro de uma transação. Graças a isso, escrever a lógica comercial transacional se torna bastante simples.

Para ser mais preciso, o gerenciamento de transações é simples em aplicativos monolíticos que acessam um único banco de dados. Se o aplicativo usar vários bancos de dados e intermediários de mensagens, esse processo se tornará mais difícil. Bem, na arquitetura de microsserviço, as transações abrangem vários serviços, cada um com seu próprio banco de dados. Em tais circunstâncias, o aplicativo deve usar um mecanismo de transação mais sofisticado. Como você verá em breve, a abordagem tradicional para transações distribuídas não é viável em aplicativos modernos. Os sistemas baseados em microsserviços devem usar a narrativa.

Mas antes de passar para as narrativas, vamos ver por que o gerenciamento de transações cria tantas complexidades em uma arquitetura de microsserviço.

4.1.1 Arquitetura de microsserviço e a necessidade de transações distribuídas


Imagine que você é desenvolvedor da FTGO e é responsável por implementar a operação do sistema createOrder (). Conforme descrito no capítulo 2, esta operação deve garantir que o cliente possa fazer pedidos, verificar detalhes do pedido, autorizar o cartão bancário do cliente e criar um registro de pedido no banco de dados. A implementação dessas ações seria relativamente simples em um aplicativo monolítico. Todos os dados necessários para verificar o pedido estão prontos e disponíveis. Além disso, transações ACID podem ser usadas para garantir a consistência dos dados. Você pode simplesmente especificar a anotação @Transactional para o método de serviço createOrder ().

No entanto, executar esta operação em uma arquitetura de microsserviço é muito mais difícil. Como pode ser visto na fig. 4.1, os dados exigidos pelas operações createOrder () estão espalhados por vários serviços. createOrder () lê as informações do serviço ao consumidor e atualiza o conteúdo dos serviços de pedidos, cozinha e contabilidade.

Como cada serviço possui seu próprio banco de dados, você deve usar um mecanismo para coordenar os dados entre eles.

4.1.2 Problemas de transação distribuída


A abordagem tradicional para garantir a consistência dos dados entre vários serviços, bancos de dados ou intermediários de mensagens é o uso de transações distribuídas. O padrão de fato para o gerenciamento de transações distribuídas é o X / Open XA (consulte en.wikipedia.org/wiki/XA ). O modelo XA usa confirmação de duas fases (2PC) para garantir que todas as alterações na transação sejam salvas ou revertidas. Isso requer bancos de dados, intermediários de mensagens, drivers de banco de dados e APIs de mensagens para estar em conformidade com o padrão XA, e também é necessário um mecanismo de comunicação entre processos que distribua identificadores globais de transações XA. A maioria dos bancos de dados relacionais é compatível com XA, assim como alguns intermediários de mensagens. Por exemplo, um aplicativo baseado em Java EE pode executar transações distribuídas usando o JTA.

imagem

Apesar de sua simplicidade, as transações distribuídas têm vários problemas. Muitas tecnologias modernas, incluindo bancos de dados NoSQL, como MongoDB e Cassandra, não são compatíveis. As transações distribuídas não são suportadas por alguns intermediários de mensagens modernos, como o RabbitMQ e o Apache Kafka. Portanto, se você decidir usar transações distribuídas, muitas ferramentas modernas não estarão disponíveis para você.

Outro problema com transações distribuídas é que elas são uma forma de IPC síncrono, o que prejudica a disponibilidade. Para que uma transação distribuída seja confirmada, todos os serviços envolvidos nela devem estar acessíveis. Conforme descrito no Capítulo 3, a acessibilidade do sistema é um produto da acessibilidade de todos os participantes da transação. Se dois serviços com disponibilidade de 99,5% participarem de uma transação distribuída, a disponibilidade geral será de 99%, o que é muito menor. Cada serviço adicional reduz o grau de disponibilidade. Eric Brewer formulou o teorema da CAP, que afirma que um sistema pode ter apenas duas das três propriedades a seguir: consistência, acessibilidade e resistência à partição (en.wikipedia.org/wiki/CAP_ Theorem). Hoje, os arquitetos favorecem sistemas acessíveis, sacrificando a consistência.

À primeira vista, as transações distribuídas podem parecer atraentes. Do ponto de vista do desenvolvedor, eles têm o mesmo modelo de software que as transações locais. Porém, devido aos problemas descritos anteriormente, essa tecnologia não é viável em aplicativos modernos. O Capítulo 3 mostrou como enviar mensagens como parte de uma transação de banco de dados sem usar transações distribuídas. Para resolver o problema mais complexo de garantir a consistência dos dados em uma arquitetura de microsserviço, o aplicativo deve usar um mecanismo diferente com base no conceito de serviços assíncronos fracamente acoplados. E aqui as narrativas são úteis.

4.1.3 Use o modelo Storytelling para manter a consistência dos dados


A narração de histórias é um mecanismo que garante a consistência dos dados em uma arquitetura de microsserviço sem o uso de transações distribuídas. A narrativa é criada para cada equipe do sistema que precisa atualizar dados em vários serviços. Essa é uma sequência de transações locais, cada uma atualizando dados em um serviço, usando as estruturas e bibliotecas familiares para transações ACID mencionadas anteriormente.

Modelo de contação de histórias

Garante a consistência dos dados entre os serviços usando uma sequência de transações locais coordenadas usando mensagens assíncronas. Consulte microservices.io/patterns/data/saga.html .

A operação do sistema inicia o primeiro estágio da narração. A conclusão de uma transação local leva ao seguinte. Na seção 4.2, você verá como a coordenação dessas etapas é realizada usando mensagens assíncronas. Uma vantagem importante do sistema de mensagens assíncronas é que ele garante que todos os estágios da narração sejam concluídos, mesmo que um ou mais participantes estejam inacessíveis.

As narrações têm várias diferenças importantes nas transações do ACID. Antes de tudo, eles não têm isolamento (para mais informações, consulte a Seção 4.3). Além disso, como cada transação local captura suas alterações, para reverter a história, você deve usar transações compensatórias, as quais discutiremos mais adiante nesta seção. Considere um exemplo de narrativa.

Exemplo Narrativo: Criando um Pedido


Neste capítulo, usamos a narrativa Criar ordem como exemplo (Fig. 4.2). Ele implementa a operação createOrder (). A primeira transação local é acionada por uma solicitação de criação de pedido externa. As cinco transações restantes são acionadas uma após a outra.

imagem

Essa narrativa consiste nas seguintes transações locais.

1. Solicite o serviço. Cria um pedido com o estado APPROVAL_PENDING.

2. Consumidor de serviço. Verifica se o cliente pode fazer pedidos.

3. serviço de cozinha. Verifica os detalhes do pedido e cria uma solicitação com o status CREATE_PENDING.

4. Serviço de contabilidade. Autoriza o cartão bancário de um cliente.

5. Serviço de cozinha. Altera o status de um aplicativo para AWAITING_ACCEPTANCE.

6. Ordem de Serviço. Altera o status do pedido para APROVADO.

Na Seção 4.2, mostrarei como os serviços envolvidos na história interagem entre si usando mensagens assíncronas. O serviço publica uma mensagem após a conclusão de uma transação local. Isso inicia a próxima etapa da narração e permite não apenas alcançar uma coesão fraca dos participantes, mas também garantir a plena implementação da narração. Mesmo que o destinatário esteja temporariamente indisponível, o broker armazena em buffer a mensagem até que ela possa ser entregue.

As narrativas parecem simples, mas seu uso está associado a algumas dificuldades, principalmente à falta de isolamento entre elas. A solução para o problema está descrita na seção 4.3. Outro aspecto não trivial é a reversão de alterações quando ocorre um erro. Vamos ver como isso é feito.

Narrativas usam transações de compensação para reverter alterações


As transações ACID tradicionais têm um ótimo recurso: a lógica comercial pode reverter facilmente uma transação se uma violação da regra comercial for detectada. Ele simplesmente executa o comando ROLLBACK e o banco de dados cancela todas as alterações feitas até o momento. Infelizmente, a história não pode ser revertida automaticamente, porque em cada estágio captura as alterações no banco de dados local. Isso, por exemplo, significa que, no caso de uma falha na autorização de um cartão bancário no quarto estágio da narrativa Criar Pedido, o aplicativo FTGO deve cancelar manualmente as alterações feitas nos três estágios anteriores. Você deve escrever as chamadas transações de compensação.

Suponha que a transação (n + 1) -ésima na história falhe. É necessário neutralizar as consequências das n transações anteriores. No nível conceitual, cada um desses estágios de Ti possui sua própria transação compensadora Ci, que cancela o efeito do Ti. Para compensar o efeito dos primeiros n estágios, a narrativa deve executar cada transação Ci na ordem inversa. A sequência é assim: T1 ... Tn, Cn ... C1 (Fig. 4.3). Neste exemplo, a etapa Tn + 1 falha, o que requer o cancelamento das etapas T1 ... Tn.

imagem

A narrativa realiza transações compensatórias na ordem inversa às originais: Cn ... C1. Aqui, o mesmo mecanismo de execução seqüencial opera como no caso do Ti. A terminação de Ci deve iniciar Ci - 1.

Veja, por exemplo, a narrativa de Criar pedido. Pode falhar por vários motivos.

1. Informações incorretas sobre o cliente ou o cliente não tem permissão para criar pedidos.

2. Informações incorretas sobre o restaurante ou o restaurante não pode aceitar o pedido.

3. Incapacidade de autorizar o cartão bancário de um cliente.

No caso de uma falha na transação local, o mecanismo de coordenação da narrativa deve executar etapas compensatórias que rejeitem a ordem e possivelmente a ordem. Na mesa 4.1 as transações compensatórias são coletadas para cada estágio da narrativa Criar Pedido. Note-se que nem todas as etapas requerem uma transação compensatória. Isso se aplica, por exemplo, às operações de leitura, como confirmConsumerDetails () ou à operação authorizeCreditCard (), todas as etapas após as quais sempre são bem-sucedidas.

imagem

Na Seção 4.3, você descobrirá que os três primeiros estágios da narrativa Criar Pedido são chamados de transações disponíveis para compensação, porque as etapas a seguir podem falhar. O quarto passo é chamado de transação de virada, porque os próximos passos nunca falham. As duas últimas etapas são chamadas de transações repetíveis, porque sempre terminam com êxito.

Para entender como as transações compensatórias são usadas, imagine uma situação em que a autorização do cartão bancário de um cliente falha. Nesse caso, a narrativa executa as seguintes transações locais.

1. Solicite o serviço. Cria um pedido com o estado APPROVAL_PENDING.

2. Consumidor de serviço. Verifica se o cliente pode fazer pedidos.

3. serviço de cozinha. Verifica os detalhes do pedido e cria uma solicitação com o status CREATE_PENDING.

4. Serviço de contabilidade. Faz uma tentativa malsucedida de autorizar o cartão bancário de um cliente.

5. Serviço de cozinha. Altera o status do aplicativo para CREATE_REJECTED.

6. Ordem de Serviço. Altera o status do pedido para REJECTED.

O quinto e o sexto estágios são transações compensatórias que cancelam as atualizações feitas pelos serviços de Cozinha e, consequentemente, o Pedido. A lógica narrativa coordenadora é responsável pela sequência de transações diretas e compensatórias. Vamos ver como isso funciona.

Sobre o autor


Chris Richardson é desenvolvedor, arquiteto e autor de POJOs em Ação (Manning, 2006), que descreve como criar aplicativos Java em nível corporativo usando as estruturas Spring e Hibernate. Ele possui os títulos honorários de Java Champion e JavaOne Rock Star.

Chris desenvolveu a versão original do CloudFoundry.com, uma implementação inicial da plataforma Java PaaS para o Amazon EC2.

Agora ele é considerado um líder ideológico reconhecido no mundo dos microsserviços e fala regularmente em conferências internacionais. Chris criou o microservices.io, que contém padrões de design de microsserviço. Ele também realiza consultas e treinamentos em todo o mundo para organizações que estão migrando para a arquitetura de microsserviços. Chris está atualmente trabalhando em sua terceira startup, Eventuate.io. É uma plataforma de software para o desenvolvimento de microsserviços transacionais.

»Mais informações sobre o livro podem ser encontradas no site do editor
» Conteúdo
» Trecho

Cupom de 25% de desconto para vendedores ambulantes - Microservice Patterns
Após o pagamento da versão impressa do livro, um livro eletrônico é enviado por e-mail.

Source: https://habr.com/ru/post/pt462519/


All Articles