Os 8 Princípios Reativos de uma Arquitetura Distribuída

A construção de aplicações em ambiente cloud traz alguns desafios que precisam ser endereçados para o sucesso de um produto: estado inconsistente, rede muitas vezes não confiável, quedas de serviços, tudo isso são exemplos de problemas que o desenvolvedor de hoje deve se preocupar para conseguir tirar o máximo de proveito dos benefícios das plataformas de nuvem, como elasticidade, uso eficiente de recursos, custos adaptáveis, entre outros.

Esses problemas não devem ser endereçados exclusivamente nos serviços de infraestrutura já disponibilizados pelas plataformas de cloud (containers, bancos de dados, balanceadores de carga, etc.), mas também é uma preocupação que deve ser tratada na arquitetura e na codificação das aplicações.

Recentemente foi publicado um guia contendo alguns princípios e padrões que definem uma arquitetura reativa e complementa o que foi descrito no Manifesto Reativo. Esse material escrito por Jonas Bonér, criador do framework Akka, traz os princípios que habilitam a construção de sistemas distribuídos e altamente concorrente, de forma que eles possam ter o desempenho esperado, serem dimensionáveis e resilientes, além de permitirem um uso de recursos computacionais mais eficiente e facilitarem a sua manutenção e operação.

Isso se encaixa perfeitamente nos desafios que a construção e operação de aplicações Cloud Native trazem. Adicionar a reatividade em aplicações cloud implica nos seguintes benefícios:

  • Melhor uso de recursos computacionais, como threads e CPU.
  • Melhores mecanismos para controle do ciclo de vida das aplicações, recuperação de falhas e mudanças estruturais dos sistemas.
  • Melhor ferramental para operar as aplicações.

São oito os princípios que guiam a construção de aplicações do tipo Reactive Cloud Native e neste artigo eu vou resumi-los.

I. Mantenha-se responsivo

Este primeiro princípio está muito ligado à característica principal e mais visível, do ponto de vista dos usuários, que os Sistemas Reativos possuem: a responsividade. É a partir desse princípio que todos os conceitos de reatividade são fundamentados.

Entregar ao usuário do sistema um serviço consistente fará com que a sua confiança e o seu engajamento aumente, por isso esse princípio rege que um sistema reativo deve dar uma resposta rápida e eficiente sempre, mesmo que isso signifique eventualmente retornar uma mensagem de erro, ou ainda devolver um retorno que não seja exatamente completo, mas que traga algum valor para a pessoa.

II. Aceite a incerteza

Os sistemas distribuídos se configuram num domínio de aplicações não determinísticas. Não temos a garantia, por exemplo, que uma informação que esteja sendo processada no nó A esteja atualizada com outro dado que esteja sendo processado no nó B.

Um exemplo é sabermos a data e hora de um processamento: as máquinas do ambiente distribuído podem estar com seus relógios ligeiramente dessincronizados, o que leva a inconsistências no sistema, em uma necessidade de ordenação de registros. Ao invés de considerar a data e hora do servidor local, podemos utilizar algoritmos como relógios de Lamport para conseguir discernir a ordem de eventos.

É um mundo de redes de comunicação não-confiáveis, problemas de hardware e latência oscilante. Mesmo algoritmos que pretendem corrigir essas distorções podem se mostrar lentos e com problemas de escalabilidade (por exemplo, Two-Phase Commit). Por isso, muitas vezes é necessário abrir mão de uma consistência forte para se alcançar uma responsividade desejada, aceitando incertezas.

Sistemas reativos devem ser construídos com propriedades para ignorar se um registro é processado em batch ou não (associatividade), se a ordem de um registro veio diferente da esperada (comutatividade) ou mesmo se uma requisição seja enviada em duplicidade para processamento (idempotência).

III. Adote as falhas

Diferente do erros, que são condições naturais e tratáveis de uma aplicação, a falha denota uma situação na qual um componente não consegue mais responder às requisições.

Aplicações reativas devem ser desenhadas considerando que falhas sempre irão acontecer e devem possuir mecanismos que minimizem os problemas, por exemplo, o uso de técnicas de self-healing e bulkheading, e abordagens do tipo let it crash, que recriam automaticamente os componentes com problemas, muito comuns em implementações do Actor Model como o Akka e Erlang.

IV. Afirme a autonomia

Este princípio é o mesmo que dita o conceito de microsserviços: a autonomia.

Aplicações reativas precisam que suas fronteiras sejam bem definidas, garantindo que se tenha conhecimento de quem é o dono dos dados e quais são os contratos de comunicação, através de protocolos bem conhecidos e documentados. A independência é necessária dado que essas aplicações devem mudar sem interferir o ecossistema, além de isolar qualquer falha dentro das suas fronteiras, o que também visa não impactar outros serviços.

V. Consistência personalizada

Como comentamos, a busca por uma consistência forte pode trazer efeitos indesejados em relação ao desempenho e disponibilidade dos sistemas. Por isso, é importante saber exatamente quando uma das peças do sistema deve ter esse nível de consistência para evitar aplicar esse conceito em lugares em que ele não traga valor.

A chave é saber aplicar a correta consistência, dando preferência sempre a consistência eventual, o que permite que o sistema permaneça disponível e possa se recuperar automaticamente em casos de falha.

VI. Desacoplamento temporal

Processos que precisam esperar o retorno de requisições externas gastam recursos computacionais de forma desnecessária. Dessa forma, implementar o desacoplamento temporal, através de técnicas como mensagens assíncronas, permite com que as aplicações utilizem de forma mais eficiente as threads e CPUs.

Trabalhar de forma assíncrona remove a necessidade em assumir que um componente do qual se depende sempre está em pé, atendendo as requisições. Isso é muito importante para alcançarmos o princípio da autonomia.

VII. Desacoplamento espacial

Diferente do desacoplamento temporal, que abstrai quando uma aplicação está disponível, o desacoplamento espacial abstrai onde essa aplicação está executando, permitindo que um processo que atenda a uma requisição esteja em qualquer ponto da rede, trazendo uma maior flexibilidade para a hospedagem dos serviços.

Essa flexibilidade permite com que um serviço seja replicado em vários nós, trazendo uma maior disponibilidade, distribuição de carga e resiliência da aplicação.

Um exemplo do desacoplamento temporal é uma característica chamada location transparency, presente no Actor Model. Ela é uma abstração que permite que um processo faça a comunicação com outro processo sem saber detalhes de onde este último se encontra instanciado. Pode ser tanto na mesma máquina que ele está rodando, em outro servidor, ou até mesmo em outro data center.

VIII. Gerencie o dinamismo

Este último princípio está relacionado com a elasticidade, nos lembrando de utilizar os recursos de forma eficiente, alocando-os conforme a demanda.

Aqui podemos nos valer das ferramentas e métricas que são comuns às plataformas de cloud, permitindo o uso de algoritmos reativos e até mesmo preditivos para alocar ou desalocar máquinas e distribuir melhor as cargas das aplicações. Aplicações construídas com isso em mente são melhor desenhadas para considerar técnicas como sharding.

Conclusão

Podemos ver que todos os princípios estão bem relacionados com as quatro características dos sistemas reativos: a responsividade, elasticidade, resiliência e a orientação a mensagens. Quem trabalha em plataformas de cloud ou distribuídas encontra similaridade com vários conceitos já bem difundidos, como desacoplamento, mas a importância desse trabalho, disponível no site do Reactive Foundation (https://principles.reactive.foundation/principles/index.html), é elencar o que os desenvolvedores e arquitetos devem ter sempre em mente no momento de criar as aplicações.

Citei bastante neste artigo o Actor Model, que possui uma ligação muito forte com sistemas reativos. Para saber mais sobre ele, segue o link de uma live do Canal dotNET que participei ano passado, falando sobre o framework Microsoft Orleans e introduzindo o modelo de atores.


Comentários

Postagens mais visitadas deste blog

Trocando configurações padrão do Live TIM

Uma proposta de Clean Architecure com Modelo de Atores

Testes automatizados em sistemas autenticados com certificados digitais, usando Selenium e PhantomJS