A revolução reativa

Programação reativa

Um novo paradigma está surgindo, e com ele muitas mudanças virão. A programação reativa já está se tornando uma realidade, e mais cedo ou mais tarde todos serão atingidos.

Dentro dessa série de artigos, nós vamos falar da programação reativa e do novo modelo de desenvolvimento que está emergindo com ela. De onde vem, por quê, o que vai mudar nos testes, nas linguagens de desenvolvimento, nas performances, etc. Nós tentaremos responder a essas e outras questões.

O que é o modelo reativo?

O manifesto reativo define que uma aplicação reativa sustenta-se sobre 4 pilares inter-relacionados: orientado à eventos, responsivo, escalável e resiliente.

Uma aplicação é reativa se ela é orientada a eventos, capaz de fornecer uma excelente experiência ao usuário, capaz de utilizar melhor o potencial das máquinas, e de tolerar melhor os erros e falhas.

O conceito mais forte é a orientação à eventos, já que ele determina o restante. Um modelo reativo é um modelo de desenvolvimento orientado à eventos. Há várias formas de descrevê-lo, dependendo da perspectiva.

O modelo poderia ser chamado de:

  • "event-driven", orientado à eventos, dirigido pelos eventos
  • "reativo", que reage aos estímulos
  • "push based application", os dados são empurrados à medida que ficam disponíveis
  • ou mesmo "Hollywood": "não nos procure, nós entraremos em contato"

Mas o termo "reativo" prevalece.

É um modelo de arquitetura muito relevante para aplicações que interagem em tempo real. Por exemplo:

  • Documentos compartilhados (Google docs, Office 360)
  • Redes sociais (difusão de fluxo, Like/+1)
  • Análises financeiras (fluxo do mercado, leilões)
  • Informações agregadas (rotas de tráfego ou de transportes em comum, poluição, dicas, vagas de estacionamento, etc.)
  • Jogos multiplayers
  • Abordagens multi-canal (o mesmo usuário usa seu PC, celular e tablet)
  • Sincronização das aplicações móveis (ao mesmo tempo em vários dispositivos de cada utilizador)
  • APIs abertas ou privadas (utilização imprevisível)
  • Monitoração de sensores (GPS, sensores no chão, objetos conectados)
  • Fluxos massivos de usuários (eventos esportivos, vendas, TV pública, Startups crescendo, a abertura de uma nova plataforma móvel, etc.)
  • Comunicação direta (chat, hang-out)
  • Gerenciamento eficaz de algoritmos complexos (reserva de lugares, gestão de grafos, web semântica, etc.)
  • etc.

Um dos pontos chave de todas essas aplicações é a gestão da latência. Para que uma aplicação seja responsiva, a latência deve ser a menor possível na percepção do usuário.

Existem diversas arquiteturas possíveis para resolver a escalabilidade e a resiliência, mas agora vamos nos concentrar na latência.

Diminuir a latência

Há muitos anos os processos concorrentes são executados em threads diferentes. Os programas clássicos são sequências de instruções que são executadas de forma linear em uma thread. Para realizar todas as tarefas que lhe são requisitadas, um servidor gera uma quantidade de threads. Mas essas threads infelizmente passam a maior parte do seu tempo esperando por uma chamada de rede, uma leitura de disco, ou uma query numa base de dados.

Na verdade existem dois tipos de threads: as soft-threads e as hard-threads. As soft-threads são as simulações de processos concorrentes, que se alternam no uso de porções da CPU para cada processo. As hard-threads são realmente os processos concorrentes, executados por diferentes núcleos (cores) do processador. As soft-threads são boas porque permitem que as máquinas executem simultaneamente muito mais threads do que os núcleos que elas realmente têm.

No entanto, para otimizar a performance, os fabricantes de CPU recomendam:

  • Criar um thread-pool com o número de hyper-threads e reutilizar as threads
  • Evitar chamadas ao kernel
  • Evitar compartilhar dados entre as threads

O modelo reativo recomenda evitar sempre que possível as soft-threads, usando somente as hard-threads. Isso permite explorar melhor a capacidade dos processadores modernos.

Há muito tempo que as tecnologias de rede presentes nos roteadores modernos exploram esse modelo de desenvolvimento com ótimos resultados.

A abordagem reativa visa democratizar esse princípio de desenvolvimento.

Para poder reduzir o número de threads, é preciso que a CPU não seja compartilhada com base no tempo, mas baseada nos eventos. Cada solicitação resulta num processamento de um pedaço de código. Esses processos não podem ser bloqueadores, para que possam liberar a CPU o mais rápido possível e processar o próximo evento.

Para viabilizar isso, é necessário mudar todas as camadas do sistema: dos sistemas operacionais até as linguagens de programação, passando pelos frameworks, drivers de hardware e bases de dados. Todas as camadas do sistema estão mudando para permitir esse tipo de arquitetura.

Esse tipo de arquitetura permite:

  • Reduzir a latência;
  • Melhorar a performance, aumentando o paralelismo;
  • Gerenciar melhor os picos de carga, eliminando o limite arbitrário de processos simultâneos;
  • Explorar melhor o número de núcleos das CPUs;
  • Ser capaz de gerenciar o fluxo de processamento (além de requests / responses normais);
  • Reduzir o consumo de memória.

Essas melhorias permitem concentrar um maior número de usuários por servidor. Na mesma proporção, isso reduz também o custo na núvem (cloud).

No nível técnico isso significa:

  • Não há custos de sincronização dos processos concorrentes (a menos que haja um só core)
  • Menor uso de memória para manter os estados (nada de pilhas)
  • Melhor agendamento de processos, baseado nas prioridades reais das aplicações

O mundo reativo permite não limitar o número de usuários simultâneos por um parâmetro arbitrário fixo no pool de threads. Essa técnica tem mais chances de reagir bem aos picos de carga. Esses ganhos potenciais às vezes são contestados em alguns estudos. A evolução dos SOs, a implementação das threads, processadores e máquinas virtuais, o tipo de JVM, poderiam afetar os benchmarks feitos. Enfim, é uma combinação sutíl entre esses elementos que permite comparar uma arquitetura à outra. Para um bom benchmark, seria então necessário criar cenários de desenvolvimento muito diferentes: um reativo, outro clássico. Isso pode explicar porque é difícil de achar benchmarks confiáveis.

Como parte da pesquisa que temos realizado, que apresentamos no PerfUG, os ganhos são notáveis para uma arquitetura processando um fluxo elevado. Da mesma maneira, nossos trabalhos em J2EE para uma utilização Web clássica confirmam os ganhos de performance e a escalabilidade da arquitetura. Veja onde podemos ir com um desktop comum e uma aplicação Java reativa para web.

Certamente que uma estrutura de dados evitando bloqueios ajuda muito na performance do sistema. Os novos modelos de dados funcionais são bons aliados dos modelos reativos.

Entre os novos softwares que fazem sucesso, vários usam o modelo reativo internamente, entre eles: Redis, Node.js, Storm, Play, Vertx, Axon ou Scala.

Da mesma maneira, os gigantes da web publicaram vários feedbacks das suas experiências de migração para esse modelo: Coursera, Gilt, Groupon, Klout, Linkedin, NetFlix, Paypals, Twitter, WallMart ou Yahoo.

Por que agora?

"O software se torna lento mais rapidamente do que o hardware se torna rápido" Niklaus Wirth - 1995

O modelo reativo não é novo. Ele é utilizado dentro de todos os frameworks de interface de usuário desde a invenção do mouse. Cada clique ou entrada no teclado gera um evento. Mesmo o Javascript client-side usa esse modelo, mas como não há threads nessa linguagem, não é possível ter várias requisições AJAX simultaneamente. Tudo funciona com a ajuda de callbacks e eventos.

As arquiteturas de desenvolvimento atuais são o resultado de uma série de etapas e evoluções. Alguns conceitos fortes foram introduzidos e utilizados abundantemente antes de serem substituídos por novas idéias. O ambiente evoluiu da mesma maneira, e as respostas não são mais as mesmas.

Será que nossos sistemas já estão no limite? Será que ainda há espaço a ser conquistado e ganhos de performance a se descobrir?

Existe nos nossos sistemas, um enorme reservatório inexplorado de energia. Houve um aumento de 20 vezes no número de usuários mobile, desde a democratização dos celulares. Mas será que faz sentido aumentar o número de servidores nessa mesma proporção? E como o seu TI vai gerenciar esse crescimento? É melhor rever os planos e tentar explorar esse potencial...

Existem ciclos de processamento disponíveis, isso é bem evidente, e é aqui que está o potencial a ser explorado. Como os nossos programas passam boa parte do tempo à espera do disco, da rede ou da base de dados, nós nunca conseguimos explorar todo o potencial dos servidores.

O modelo "reativo" agora está disponível para todos. Ele está cada vez mais integrado às linguagens de desenvolvimento modernas.

Novos padrões de desenvolvimento estão sendo propostos. A gestão da latência e da performance são feitas desde o início do desenvolvimento. Não é mais um problema a ser enfrentado apenas quando já está tudo feito e não é mais possível mudar a arquitetura da aplicação.

Na OCTO nós temos a convicção de que esse modelo de desenvolvimento vai emergir nos próximos anos.

Sobre as aplicações candidatas ao modelo reativo, as aplicações baseadas no modelo request/response (HTTP/SOA/REST) podem tolerar um modelo de threads, ao contrário das aplicações baseadas em fluxo de dados, como JMS ou WebSocket, que tem muito a ganhar com o modelo baseado em eventos e algumas soft-threads.

Visão CEO - O que a sua empresa ganha?

O modelo reativo traz vantagens para sua empresa, principalmente nesses aspectos:

  • World class UX com maior retenção de usuários graças a resposta rápida a eventos com baixa latência;
  • Redução de custos com servidores, uma vez que aplicações reativas exploram melhor a capacidade dos servidores multicore;
  • Modelo facilmente escalonável;
  • Redução de interrupções de serviço, uma vez que aplicações reativas não possuem single point of failture.

Veremos em outros artigos de onde vem esta evolução, e os impactos sobre todas as camadas dos sistemas (base de dados, mainframe, roteamento, failover, alta-disponibilidade, etc.).