O que é Reactive Design Patterns?

reactive

Os princípios centrais do Reactive Design são descritos no Reactive Manifesto. O Reactive Design em si é muito amplo para se chamado de um padrão, da mesma forma que o programação orientada a objetos é muito ampla para ser chamada como um padrão. Entendemos que é uma abordagem. E dentro desta abordagem, existem muitos padrões. Neste tópico descreveremos de forma resumida o Reactive Design.

Reactive

– Um conjunto de princípios de design

O Reactive se tornou um termo popular e agora está sendo associado a várias coisas diferentes e com fins diferentes – combinado com palavras como “fluído”, “leve” e “em tempo real”.

Reactive é um conjunto de princípios de design para criar sistemas coesos. É uma maneira de pensar sobre arquitetura e design de sistemas em um ambiente distribuído em que técnicas de implementação, ferramentas e padrões de design são componentes de um conjunto maior.

Pense no Reactive como sendo um conjunto de serviços individuais otimizados, cada qual para a sua função, que quando combinados geram um resultado pretendido. Cada parte está programada para executar a sua tarefa, por isso reativo.

Um Sistema Reativo é baseado em um estilo arquitetônico que permite que esses múltiplos serviços individuais se aglutinem como uma única unidade e reajam ao ambiente ao mesmo tempo em que se mantêm cientes um do outro.

Quando as pessoas falam sobre Reactive no contexto de desenvolvimento e design de software, elas geralmente querem dizer uma de três coisas:

  • Sistemas reativos (arquitetura e design);
  • Programação reativa (baseada em evento declarativo);
  • Programação Reativa Funcional (FRP).

O principal impulsionador por trás dos sistemas modernos é a noção de capacidade de resposta: o reconhecimento de que, se o cliente / cliente não obtiver valor em tempo hábil, ele irá para outro lugar. Fundamentalmente, não há diferença entre não obter valor e não obter valor quando é necessário.

Para facilitar a capacidade de resposta, dois desafios precisam ser enfrentados: ser responsivo sob falha, definido como resiliência e ser responsivo sob carga, definido como elasticidade. O Reactive Manifest descreve que, para conseguir isso, o sistema precisa ser orientado por mensagens.

reactive-tratos

Programação Reativa Funcional (FRP)

A Programação Reativa Funcional, comumente chamada de “FRP” (Functional Reactive Programming), é freqüentemente mal compreendida. O FRP foi definido com muita precisão há 20 anos por Conal Elliott. Mais recentemente, o termo foi usado incorretamente para descrever tecnologias como Elm, Bacon.js e Extensões Reativas (RxJava, Rx.NET, RxJS), entre outras. A maioria das bibliotecas que afirmam suportar FRP na verdade estão falando sobre Programação Reativa.

Programação Rativa

A Programação Reativa, como dito acima, não deve ser confundida com a Programação Reativa Funcional, é um subconjunto da Programação Assíncrona e um paradigma em que a disponibilidade de novas informações impulsiona a lógica em vez de ter um fluxo de controle orientado por um thread-de-execução.

Ela suporta a decomposição do problema em várias etapas discretas, em que cada uma pode ser executada de maneira assíncrona e não-blocante, e depois composta para produzir um fluxo de trabalho, possivelmente ilimitado em suas entradas ou saídas.

Assíncrono neste contexto significa que o processamento de uma mensagem ou evento está acontecendo em algum momento arbitrário, possivelmente no futuro.

Essa é uma técnica muito importante em Programação Reativa, pois permite a execução sem bloqueio – em que os segmentos de execução que competem por um recurso compartilhado não precisam esperar pela execução do outro, e podem, como tal, realizar outros trabalhos úteis enquanto o recurso estiver ocupado.

Mas afinal qual a diferença?

A programação reativa é geralmente orientada por eventos, em contraste com os sistemas reativos, que são orientados por mensagens.

A API para programação de bibliotecas Reactive geralmente são:

  • Baseado em retorno de chamada – onde retornos de chamada anônimos e com efeitos colaterais são anexados a fontes de eventos e estão sendo chamados quando os eventos passam pela cadeia de fluxo de dados;
  • Declarativa – através da composição funcional, geralmente usando combinadores bem estabelecidos como map, filter, fold etc.

Os benefícios (e limitações) da programação reativa

Os principais benefícios da Programação Reativa são: maior utilização de recursos computacionais em hardware multicore e multi-CPU.

Um benefício secundário é a produtividade do desenvolvedor, pois os paradigmas tradicionais de programação têm se esforçado para fornecer uma abordagem direta e de fácil manutenção para lidar com computação e IO assíncrona e não-blocante. Programação reativa resolve a maioria dos desafios aqui, uma vez que normalmente elimina a necessidade de coordenação explícita entre os componentes ativos.

Onde a programação reativa brilha está na criação de componentes e composição de fluxos de trabalho. Para aproveitar ao máximo a execução assíncrona, a inclusão da contrapressão é crucial para evitar a utilização excessiva, ou melhor, o consumo ilimitado de recursos.

Mas mesmo que a Programação Reativa seja uma peça muito útil na construção de software moderno, para raciocinar sobre um sistema em um nível mais alto, é necessário usar outra ferramenta: Arquitetura Reactiva – o processo de projetar Sistemas Reativos. Além disso, é importante lembrar que existem muitos paradigmas de programação e que a Programação Reativa é apenas uma delas, assim como qualquer ferramenta, ela não se destina a todos os casos de uso.

Guiado por eventos ou mensagens

Como mencionado anteriormente, a Programação Reativa concentrando-se na computação através de cadeias de fluxo de dados efêmeros, portanto tende a ser Orientada a Eventos, enquanto Sistemas Reativos concentrando-se na resiliência e elasticidade através da comunicação e coordenação de sistemas distribuídos, é orientada por Mensagens.

A principal diferença entre um sistema orientado por mensagens com componentes endereçáveis de longa duração e um modelo orientado por fluxo de dados orientado a eventos é que as mensagens são inerentemente direcionadas, já os eventos não são. As mensagens têm um destino claro e único, enquanto Eventos são fatos para outros observarem. Além disso, a mensagem é preferencialmente assíncrona, com o envio e a recepção desacoplados do remetente e do destinatário, respectivamente.

Explicando melhor…

Mensagens são necessárias para se comunicar através da rede e formam a base para comunicação em sistemas distribuídos, enquanto Eventos, por outro lado, são emitidos localmente. Isso permite manter a relativa simplicidade do modelo de programação orientada a eventos em um contexto distribuído e pode funcionar muito bem para casos de uso especializados e bem definidos (por exemplo, AWS Lambda, produtos de fluxo distribuído como Spark Streaming, Flink, Kafka e Akka Streams, e Kafka e Kinesis).

No entanto, há um trade-off: o que se ganha na abstração e na simplicidade do modelo de programação, perde-se em termos de controle.

O envio de mensagens nos força a abraçar a realidade e as restrições dos sistemas distribuídos – coisas como falhas parciais, detecção de falhas, mensagens descartadas/duplicadas/reordenadas, consistência eventual, gerenciamento de múltiplas realidades simultâneas etc.

Essas diferenças de semântica e aplicabilidade têm implicações profundas no design do aplicativo, incluindo itens como resiliência, elasticidade, mobilidade, transparência da localização e gerenciamento da complexidade dos sistemas distribuídos.

Em um Sistema Reativo, especialmente um que usa a Programação Reativa, os eventos e as mensagens estarão presentes, como se fosse uma ótima ferramenta de comunicação (mensagens), e uma ótima maneira de representar fatos (eventos).

Arquitetura dos sistemas reativos

Sistemas Ativos, como definido pelo Reactive Manifest, é um conjunto de princípios de projeto arquitetônico para a construção de sistemas modernos que estão bem preparados para atender às crescentes demandas que os aplicativos enfrentam atualmente.

Os princípios dos Sistemas Reativos definitivamente não são novos, e podem ser vistos desde os anos 70 e 80. No entanto, apenas nos últimos 5 a 10 anos que a indústria de tecnologia, forçada a repensar por “melhores práticas”, introduziu no desenvolvimento de sistemas corporativos. Isso significa aprender a aplicar o conhecimento obtido com dificuldade sobre os princípios reativos no mundo atual de multicore, computação em nuvem e Internet das coisas.

A base para um Sistema Reativo é a Passagem de Mensagens, que cria um limite temporal entre os componentes, o que permite que eles sejam dissociados no tempo. Isso permite a simultaneidade e o espaço, que permite a distribuição e a mobilidade. Esse desacoplamento é um requisito para o isolamento total entre os componentes e forma a base para Resiliência e Elasticidade.

De programas para sistemas

O mundo está se tornando cada vez mais interconectado. Os sistemas são complexos por definição. Cada um consistindo em uma infinidade de componentes, que em si mesmos também podem ser sistemas, o que significa que o software depende cada vez mais de outro software para funcionar adequadamente.

Os sistemas que criamos hoje devem ser operados em computadores pequenos e grandes, poucos e muitos, próximos uns dos outros ou a meio mundo de distância. E, ao mesmo tempo, as expectativas dos usuários tornaram-se cada vez mais difíceis de encontrar, pois a vida humana cotidiana depende cada vez mais da disponibilidade de sistemas para funcionar sem problemas.

Para fornecer sistemas dos quais os usuários e as empresas podem depender, eles precisam ser responsivos, pois não importa se algo fornece a resposta correta se a resposta não estiver disponível quando for necessária. Para conseguir isso, precisamos ter certeza de que a capacidade de resposta pode ser mantida sob falha (resiliência) e sob carga dinamicamente variável (elasticidade). Para que isso aconteça, tornamos esses sistemas baseados em mensagens, e os chamamos de sistemas reativos.

A resiliência dos sistemas reativos

Resiliência é sobre a capacidade de resposta sob falha e é uma propriedade funcional inerente do sistema, algo que precisa ser projetado para, e não algo que pode ser adicionado de forma retroativa.

Isso exige o isolamento de componentes e a contenção de falhas, a fim de evitar que as falhas se espalhem para os componentes vizinhos resultando em cenários de falhas em cascata, muitas vezes fatais.

Assim, a chave para construir sistemas resilientes e de autoajuste é permitir que as falhas sejam: contidas, retificadas como mensagens, enviadas para outros componentes (que atuam como supervisores) e gerenciadas a partir de um contexto seguro fora do componente com falha. Aqui, ser guiado por mensagens é o facilitador: afastar-se de cadeias de chamadas síncronas fortemente acopladas, frágeis e profundamente aninhadas. A ideia é dissociar o gerenciamento de falhas na cadeia de chamadas, liberando o cliente da responsabilidade de lidar com as falhas do servidor.

A elasticidade dos sistemas reativos

A Elasticidade é sobre Responsividade sob carga. O que significa que a taxa de transferência de um sistema aumenta ou diminui (por exemplo, adicionando ou removendo núcleos em uma única máquina), bem como dentro ou fora (ou seja, adicionando ou removendo nós/máquinas em um data center) automaticamente para atender à demanda variável, pois os recursos são adicionados ou removidos proporcionalmente. É o elemento essencial necessário para aproveitar o que há de melhor na computação em nuvem: permitir que os sistemas sejam eficientes em termos de recursos, econômicos, ecológicos e pagos por uso.

Os sistemas precisam ser adaptáveis, permitindo autoescalonamento sem intervenção, replicação de estado e comportamento, balanceamento de carga de comunicação, failover e atualizações, tudo sem reescrever ou mesmo reconfigurar o sistema. O facilitador para isso é a Transparência do Local: a capacidade de dimensionar o sistema da mesma forma, usando as mesmas abstrações de programação, com a mesma semântica, em todas as dimensões de escala de núcleos de CPU a datacenters.

Qual a relação dos Sistemas Reativos com a Programação Reativa?

A Programação Reativa é uma técnica para gerenciar a lógica interna e a transformação de fluxo de dados, localmente nos componentes, como uma maneira de otimizar a clareza do código, o desempenho e a eficiência de recursos. Os Sistemas Reativos, sendo um conjunto de princípios de arquitetura, coloca a ênfase na comunicação distribuída e nos fornece ferramentas para lidar com resiliência e elasticidade em sistemas distribuídos.

Um problema comum com o aproveitamento apenas da Programação Reativa é que seu acoplamento entre os estágios de computação em um programa baseado em retorno baseado em evento ou declarativo torna a Resiliência mais difícil de alcançar porque suas cadeias de transformação são frequentemente efêmeras e seus estágios, os callbacks ou combinadores, são anônimos, ou seja, não endereçavel.

Isso significa que eles geralmente lidam com sucesso ou fracasso diretamente, sem sinalizá-lo para o mundo exterior. Essa falta de endereçamento dificulta a recuperação de estágios individuais, pois normalmente não é claro onde as exceções devem ou poderiam ser propagadas. Como resultado, as falhas estão vinculadas a solicitações de clientes efêmeras em vez de à integridade geral do componente, se um dos estágios da cadeia de fluxo de dados falhar, toda a cadeia precisará ser reiniciada e o cliente notificado. Isso está em contraste com um sistema reativo orientado por mensagens, que tem a capacidade de se auto-curar sem precisar notificar o cliente.

Outro contraste com a abordagem dos Sistemas Reativos é que a Programação Reativa pura permite o desacoplamento no tempo, mas não no espaço.

Explicando melhor…

A falta de transparência na localização dificulta a escalabilidade de um programa puramente baseado em técnicas de Programação Reativa de forma elástica e, portanto, requer camadas de ferramentas adicionais no topo, como um Message Bus, Data Grid ou protocolos de rede sob medida. É aqui que a abordagem dos Sistemas Reativos orientada por mensagens brilha, já que é uma abstração de comunicação que mantém seu modelo de programação e semântica em todas as dimensões de escala e, portanto, reduz a complexidade do sistema e a sobrecarga cognitiva.

Um problema comumente citado de programação baseada em retorno de chamada é que, embora a escrita desses programas possa ser comparativamente fácil, ela pode ter consequências reais a longo prazo.

Por exemplo, sistemas baseados em retornos de chamada anônimos fornecem muito pouca percepção quando você precisa raciocinar sobre eles, mantê-los ou, o mais importante, descobrir o que, onde e por que ocorrem falhas de produção e mau comportamento.

Bibliotecas e plataformas projetadas para Sistemas Reativos aprenderam essa lição há muito tempo e estão contando com componentes endereçáveis ​​de longa duração que são mais fáceis de serem discutidos no futuro. Quando ocorrem falhas, o componente é exclusivamente identificável junto com a mensagem que causou a falha. Com o conceito de endereçamento no núcleo do modelo de componente, as soluções de monitoramento têm uma maneira significativa de apresentar os dados coletados, aproveitando as identidades que são propagadas.

Em resumo, a Programação Reativa é uma técnica de implementação muito útil, que pode ser usada em uma arquitetura reativa. Gerenciamento de fluxo de dados por meio de execução assíncrona e não-bloqueante, geralmente apenas em um único nó ou serviço. Uma vez que há vários nós, é necessário começar a pensar muito sobre coisas como consistência de dados, comunicação de nós cruzados, coordenação, controle de versão, orquestração, gerenciamento de falhas, separação de preocupações e responsabilidades etc.

Velocidade de transmissão de dados e a Programação e Sistemas Reativos

O Fast Data Streaming (Processamento de fluxo distribuído) é, do ponto de vista do usuário, geralmente orientado a eventos usando abstrações locais e baseadas em fluxo, expondo APIs de usuário final que dependem de construções de Programação Reativa, como combinadores funcionais e retornos de chamada.

Em seguida, sob a API do usuário final, ele geralmente usa a passagem de mensagens e os princípios de sistemas reativos entre nós, suportando um sistema distribuído de estágios de processamento de fluxo, logs de eventos duráveis e protocolos de replicação, embora essas partes geralmente não sejam expostas pelo desenvolvedor. É um bom exemplo do uso da Programação Reativa no nível do usuário e dos Sistemas Reativos no nível do sistema.

Internet das Coisas, dispositivos móveis e Programação e Sistemas Reativos

A Internet das Coisas, com sua explosão de sensores conectados, gadgets e dispositivos, cria desafios em como lidar com todos esses dispositivos simultaneamente conectados que produzem muitos dados que precisam ser recuperados, agregados, analisados ​​e retirados os dispositivos, tudo isso mantendo a capacidade de resposta geral do sistema. Esses desafios incluem o gerenciamento de rajadas significativas de tráfego no recebimento de dados do sensor, processamento de grandes quantidades de dados (em processos em lote ou em tempo real) e simulações dispendiosas de padrões de uso reais. Algumas implantações de IoT (Internet of Things), além disso, exigem que os serviços de backend gerenciem os dispositivos, não apenas consumam os dados enviados dos dispositivos.

Ao criar serviços a serem usados ​​por potencialmente milhões de dispositivos conectados, é necessário um modelo que lide com o fluxo de informações em escala. Há uma necessidade de estratégias para lidar com falhas de dispositivos, para quando as informações são perdidas e quando os serviços falham. Os sistemas de backend que gerenciam tudo isso precisam ser capazes de escalar sob demanda e ser totalmente resilientes, em outras palavras, há necessidade de Sistemas Reativos.

Ter muitos sensores gerando dados e não conseguir lidar com a taxa com a qual esses dados chegam indica a necessidade de implementar a contrapressão para dispositivos e sensores. Analisando o fluxo de dados de ponta a ponta de um sistema IoT: armazenamento de dados, limpeza, processamento, execução de análises; e tudo sem qualquer interrupção de serviço, com serviços assíncronos (sem bloqueio os fluxos de pressão reversa). É aí que a programação reativa realmente brilha.

Aplicativos tradicionais Web e a Programação e Sistemas Reativos

Aplicações Web podem se beneficiar enormemente de um estilo de desenvolvimento de Programação Reativa, possibilitando a composição de fluxos de trabalho de solicitação-resposta envolvendo ramificação de chamadas de serviço, busca de recursos de forma assíncrona e composição de respostas e subseqüentes empacotamentos para o cliente. Mais recentemente, o envio de servidor para Eventos enviados pelo servidor e WebSockets tem se tornado cada vez mais usado, e executar isso em escala requer uma maneira eficiente de manter muitas conexões abertas e onde o IO não bloqueia. A Programação reativa tem ferramentas para isso, mais especificamente “Streams and Futures”, que torna simples fazer transformações não-blocantes e assíncronas e enviá-las aos clientes. A Programação Reativa também pode ser valiosa na camada de acesso a dados, atualizando e consultando dados de maneira eficiente em recursos, preferencialmente usando bancos de dados SQL ou NoSQL com drivers assíncronos.

Os aplicativos da Web também se beneficiam do design do Sistema Reativo para coisas como: armazenamento em cache distribuído, consistência de dados e notificações entre nós. Aplicativos da web tradicionais normalmente usam nós sem estado. Mas assim que se começa a usar o Server-Sent-Events (SSE) e WebSockets, os nós se tornam stateful, já que no mínimo, eles estão mantendo o estado de uma conexão do cliente e notificações push e precisam ser roteadas para eles de acordo. Fazer isso efetivamente requer um projeto do Sistema Reativo, já que é uma área em que o endereçamento direto dos destinatários através de mensagens é importante.

Conclusão

Neste post, descrevemos os Sistemas Reativos como sendo o objetivo final, assumindo o contexto de arquiteturas Multicore, Cloud e Mobile, com a Programação Reativa servindo como uma das ferramentas importantes.

Programação Reativa oferece produtividade para desenvolvedores, por meio de desempenho e eficiência de recursos, no nível de componente para lógica interna e transformação de fluxo de dados, enquanto Sistemas Reativos oferecem produtividade para arquitetos e DevOps, por meio de resiliência e elasticidade, no nível do sistema, para a criação de “Cloud Native” e outros sistemas distribuídos em larga escala. Recomendamos fortemente combinar as técnicas de Programação Reativa nos princípios de design de Sistemas Reativos.

*** A OctalMind é uma empresa especializada no desenvolvimento de sistemas de alta tecnologia.