Introdução à Programação Reativa com Spring WebFlux

 

O que é programação reativa?

O conceito de reatividade é algo antigo, porém pouco aplicado na prática e com o tempo foi se mostrando útil e trazendo vantagens, ao ponto que sua utilização está cada vez maior. Um exemplo do que é reatividade é quando utilizamos uma célula do excel para fazer a soma dos valores de outras duas células, por exemplo =A1+B1, quando alteramos o valor da célula A1, o resultado do cálculo dessa soma é automaticamente alterado, reagindo a alteração feita, isso pode ser considerado reatividade pura.


A programação reativa é uma grande abstração em cima do design pattern Observable, que traz o conceito dentro da programação de reagir a alterações. Programar de forma reativa é trabalhar orientado a fluxos de dados e como esses dados são alterados com o tempo, e pra trabalhar esses dados são utilizados streams de dados assíncronos.

A motivação para a programação reativa

Um aplicativo da web típico é composto por várias partes complexas e interativas . Muitas dessas interações são de natureza bloqueadora , como, por exemplo, aquelas envolvendo uma chamada de banco de dados para buscar ou atualizar dados.  Vários outros, no entanto, são independentes e podem ser executados simultaneamente, possivelmente em paralelo.

Por exemplo, duas solicitações de usuário a um servidor web podem ser tratadas por threads diferentes. Em uma plataforma com vários núcleos , isso tem um benefício óbvio em termos de tempo de resposta geral. Portanto, esse modelo de simultaneidade é conhecido como modelo thread-per-request :

Processamento Síncrono das requisições


Modelo Blocking I/O 

Embora a simultaneidade baseada em thread resolva uma parte do problema para nós, ela não faz nada para resolver o fato de que a maioria de nossas interações em um único thread ainda estão bloqueando . Além disso, os encadeamentos nativos que usamos para obter simultaneidade em Java têm um custo significativo em termos de troca de contexto.

Enquanto isso, à medida que os aplicativos da web enfrentam mais e mais solicitações, o modelo thread-per-request começa a ficar aquém das expectativas .

Consequentemente, o que precisamos é de um modelo de simultaneidade que possa nos ajudar a lidar com cada vez mais solicitações com um número relativamente menor de threads . Esta é uma das principais motivações para a adoção da programação reativa .

Simultaneidade na programação reativa

Na programação reativa o fluxo do programa se transforma de uma sequência de operações síncronas em um fluxo assíncrono de eventos.

Por exemplo, no modelo reativo, uma chamada de leitura para o banco de dados não bloqueia o thread de chamada enquanto os dados são buscados. A chamada retorna imediatamente um editor que outros podem assinar . O assinante pode processar o evento após sua ocorrência e pode ainda gerar eventos por si mesmo:

Processamento Assíncrono das requisições

Modelo Non-blocking I/O

No modelo acima, é processado uma unica solicitação por thread. Acima de tudo, a programação reativa não enfatiza quais eventos de thread devem ser gerados e consumidos. A ênfase está, em vez disso, na estruturação do programa como um fluxo de eventos assíncronos .

Loop de evento

Existem vários modelos de programação que descrevem uma abordagem reativa à simultaneidade. Nesta seção, examinaremos alguns deles para entender como a programação reativa atinge maior simultaneidade com menos threads. Um desses modelos de programação assíncrona reativa para servidores é o modelo de loop de eventos:


Acima, está um design abstrato de um loop de eventos que apresenta as ideias de programação assíncrona reativa:

  • O loop de evento é executado continuamente em um único thread , embora possamos ter tantos loops de evento quanto o número de núcleos disponíveis

  • O loop de eventos processa os eventos de uma fila de eventos sequencialmente e retorna imediatamente após registrar o retorno de chamada na plataforma

  • A plataforma pode acionar a conclusão de uma operação como uma chamada de banco de dados ou uma chamada de serviço externo

  • O loop de eventos pode acionar o retorno de chamada na notificação de conclusão da operação e enviar de volta o resultado ao chamador original

modelo de loop de evento é implementado em várias plataformas, incluindo Node.js , Netty e Ngnix . Eles oferecem escalabilidade muito melhor do que plataformas tradicionais, como Apache HTTP Server , Tomcat ou JBoss.

Spring Boot

O Spring Boot é um projeto da Spring que veio para facilitar o processo de configuração e publicação de nossas aplicações. A intenção é ter o seu projeto rodando o mais rápido possível e sem complicação. 

Spring WebFlux

Vamos explorar a pilha do lado do servidor do Spring WebFlux para entender como ela complementa a pilha da web tradicional no Spring:

O Spring MVC como conhecemos irá nos fornecer lógica imperativa, mais fácil de debugar e trabalhar em cima, também nos oferece uma gama muito grande de bibliotecas e drivers já consolidados no mercado, o webflux por default utiliza o Netty como servlet, e utiliza o modelo de event loop para concorrência, e é um grande facilitador para se utilizar endpoints funcionais.

Spring MVC e Spring-WebFlux coexistem lado a lado no Spring Framework. Cada módulo é opcional. Os aplicativos podem usar os módulos individuais ou juntos. Segue abaixo suas especificações:

Como podemos ver, o Spring WebFlux fica paralelo ao framework web tradicional no Spring e não necessariamente o substitui .

Existem alguns pontos importantes a serem observados aqui:

  • Spring WebFlux estende o modelo de programação tradicional baseado em anotação com roteamento funcional

  • Além disso, ele adapta os tempos de execução HTTP subjacentes à API Reactive Streams, tornando os tempos de execução interoperáveis

  • Portanto, é capaz de suportar uma ampla variedade de tempos de execução reativos, incluindo recipientes Servlet 3.1+ como Tomcat, Reactor, Netty ou Undertow

  • Por último, inclui WebClient , um cliente reativo e não bloqueador para solicitações HTTP que oferece APIs funcionais e fluentes

O Spring Webflux é baseado no projeto Reactor, que é uma biblioteca de programação reativa sem bloqueio para a JVM. Assim como o Reactor, ele possui dois tipos: Flux e Mono. Lembrando que na programação reativa trabalha-se com fluxos e não com dados. Sendo assim, o tipo Flux consiste em um fluxo (stream) de 0 a N elementos e o tipo Mono consiste em um fluxo (stream) de 0 ou 1 elemento apenas.

Além de todos os benefícios citados temos o WebClient que trata-se de um cliente HTTP reativo que faz parte do Spring WebFlux. Podemos usá-lo a qualquer momento em que precisarmos de comunicação baseada em REST e nos permitir criar aplicativos que sejam reativos de ponta a ponta .

Como vimos antes, os aplicativos reativos funcionam com apenas alguns threads e, portanto, não há margem para que qualquer parte do aplicativo bloqueie um thread. Portanto, o WebClient desempenha um papel vital em nos ajudar a realizar o potencial do WebFlux.

Reactor

O Reactor é uma base de programação reativa totalmente sem bloqueio para a JVM, com gerenciamento de demanda eficiente (na forma de gerenciamento de “contrapressão”). Ele se integra diretamente com as APIs funcionais do Java 8, notavelmente Completable Future, Stream e Duration. Ele oferece APIs de sequência assíncrona composíveis - Flux (para [N] elementos) e Mono (para [0 | 1] elementos) - e implementa extensivamente a especificação Reactive Streams.


Netty

Este servidor assícrono e não bloqueante é embutido por padrão no inicializador Spring Boot WebFlux. Vamos tentar ver os threads que o Netty cria por padrão. Portanto, no início, não adicionaremos quaisquer outras dependências ou usaremos WebClient. Portanto, se iniciarmos um aplicativo Spring WebFlux criado usando seu inicializador SpringBoot, podemos esperar ver alguns threads padrão que ele cria:


Observe que, além de uma thread normal para o servidor, o Netty gera uma série de threads de trabalho para processamento de solicitação . Normalmente, não são mais do que núcleos de CPU disponíveis. Esta é a saída em uma máquina quad-core. Também veríamos vários encadeamentos de manutenção típicos de um ambiente JVM, mas eles não são importantes aqui.

O Netty usa o modelo de loop de evento para fornecer simultaneidade altamente escalonável de maneira assíncrona reativa. Vamos ver como o Netty implementa o loop de eventos aproveitando o Java NIO para fornecer essa escalabilidade:


Spring WebFlux também é compatível com um contêiner de servlet tradicional, como o Apache Tomcat . Vamos ver que tipo de threads esperamos em um aplicativo WebFlux em execução no Tomcat:


Gradle

Gradle é um sistema de automação de compilação open source que se baseia nos conceitos de Apache Ant e Apache Maven e introduz uma linguagem de domínio específico baseada em Groovy em vez do XML usado pelo Apache Maven para declarar a configuração do projeto.

JUnit 5

JUnit 5 é a próxima geração de JUnit. O objetivo é criar uma base atualizada para testes do lado do desenvolvedor na JVM. Isso inclui focar em Java 8 e superior, bem como habilitar muitos estilos diferentes de teste.

Spring Data R2DBC

Spring Data R2DBC, parte da família maior Spring Data, torna mais fácil implementar repositórios baseados em R2DBC. R2DBC significa conectividade de banco de dados relacional reativo, uma incubadora para integrar bancos de dados relacionais usando um driver reativo. Spring Data R2DBC aplica abstrações Spring familiares e suporte de repositório para R2DBC. Isso torna mais fácil construir aplicativos baseados em Spring que usam tecnologias de acesso a dados relacionais em uma pilha de aplicativos reativos.


Reactor Kafka

é uma API reativa para Kafka baseada no Reactor, ele permite que as mensagens sejam publicadas e consumidas usando APIs funcionais, também com contrapressão sem bloqueio .

Estamos inscrevendo um teste reativo de tópico em Kafka e recebendo um fluxo de mensagens.

O interessante para nós são os threads que são criados :


Podemos ver acima alguns threads que não são típicos do servidor Netty .

Isso indica que o Reactor Kafka gerencia seu próprio conjunto de encadeamentos, com alguns encadeamentos de trabalho, que participam exclusivamente do processamento de mensagens do Kafka. Claro, veremos vários outros threads relacionados ao Netty e à JVM que podemos ignorar.

Os produtores Kafka usam um thread de rede separado para enviar solicitações ao broker. Além disso, eles fornecem respostas ao aplicativo em um agendador de pool de thread único .

O consumidor Kafka, por outro lado, tem um encadeamento por grupo de consumidores - que bloqueia para ouvir as mensagens recebidas. As mensagens recebidas são então planejadas para processamento em um conjunto de encadeamentos diferente.




Comentários

Postagens mais visitadas deste blog

Design Patterns

Sistema de mensageria Apache Kafka