Concrete Logo
Hamburger button

Como construir uma API Rest com Reactive Spring and Spring Boot 2.0

  • Blog
  • 28 de Julho de 2017
Share

Em poucas palavras, programação reativa são aplicações não-blocantes, orientadas por eventos que se dimensionam com um pequeno número de threads com contrapressão como ingrediente-chave, que visa garantir que os produtores não dominem os consumidores. A especificação dos fluxos reativos (também adotada em Java 9) permite a capacidade de comunicar a demanda em camadas e bibliotecas de diferentes provedores. Por exemplo, uma conexão HTTP que escreve para um cliente pode comunicar sua disponibilidade para escrever todo o caminho a montante para um repositório em um banco de dados, de modo que em um cliente HTTP lento, o repositório pode diminuir a velocidade ou mesmo pausar. Para uma introdução mais extensa à programação reativa, verifique a série de posts de Dave Syer – Notes on Reactive Programming Pat I: The Reactive Landscape.

A maioria dos aplicativos da Web Java são criados na API de Servlet, que foi originalmente criada com semântica síncrona e de bloqueio. Ao longo dos anos foi adicionado suporte para solicitações assíncronas (Servlet 3.0) e I/O sem bloqueio (Servlet 3.1). No Spring MVC foi identificado que é viável adicionar manipulação de solicitação HTTP assíncrona seletiva para aplicativo. No entanto, também descobriram que é muito difícil introduzir I/O não-blocantes dentro de um ecossistema existente de frameworks e aplicativos da Web. Isso exige mudanças muito profundas até nos contratos principais, que precisam alternar do bloqueio para a semântica assíncrona.

Uma das razões para a popularidade contínua do Spring MVC é seu modelo de programação intuitivo, baseado em anotações e em assinaturas de métodos de controllers flexíveis. Felizmente, o mesmo pode continuar a servir de base para aplicativos web reativos. Esta é a direção do esforço Spring Reactive, na qual você vai encontrar um TestController que se parece com qualquer controlador Spring MVC, mas é executado em um novo mecanismo reativo com testes de integração podendo rodar no Jetty, Undertow e Netty.

O ingrediente chave para esse esforço é a especificação de fluxos reativos, cujo objetivo é fornecer um “padrão para processamento de fluxo assíncrono com processos não-blocantes”. A especificação permite a interoperabilidade entre os provedores de componentes assíncrono, desde os servidores HTTP até estruturas da Web, drivers de banco de dados etc. Ele será incluído no JDK 9 como java.util.concurrent.Flow.

A especificação é pequena e consiste em quatro interfaces, algumas regras e um TCK (Technology Compatibility Kit) e para expor isso como uma API, no entanto, é necessária uma infraestrutura em torno dele para compor uma lógica assíncrona. O Spring Reactive usa o Reactor Core, uma pequena biblioteca que serve de base para os frameworks que desejam construir Streams Reativos.

Sem mais delongas, vamos ao código!

No exemplo de hoje, vamos criar uma pequena API utilizando Spring Boot e um banco de dados MongoDB. Caso precise de ajuda para instalação do Mongo, dê uma olhadinha nesse post que tem alguns links úteis, ok?

Vamos até o site do Spring Initializr para criarmos nosso projeto com as devidas dependências. Caso queira, pode criar também dentro do STS ou do IntelliJ, sem problemas.

Defina o nome que achar melhor para o projeto, mas atente-se aos seguintes pontos que serão necessários:

  • Mude a versão do spring boot para 2.0.0.M2;
  • Selecione as dependências: Reactive Web, Reactive MongoDB e Lombok;
  • Faça o download do projeto e abra-o na sua IDE de preferência.

OBS: Por enquanto o spring boot suporta programação reativa apenas para trabalhar com os banco de dados MongoDB, Cassandra e Redis e suas dependências estão disponíveis no Spring Initializr a partir da versão 2.0.0.SNAPSHOT com os nomes Reactive MongoDB, Reactive Cassandra e Reactive Redis.

Assim que o projeto estiver configurado em sua IDE com as dependências baixadas, vamos criar uma classe chamada Car (que será nosso modelo) com os atributos id e model e iniciar nosso desenvolvimento:

Como estamos utilizando banco de dados Mongo, devemos anotar nossa classe com @Document e as demais annotations que fazem parte do projeto Lombok (para saber um pouco mais sobre Lombok veja a documentação oficial no site: Project Lombok).

Para quem estiver utilizando IntelliJ, recomendo a instalação do plugin chamado “Lombok” para que a IDE não reclame quando criarmos um objeto que utiliza as anotações do Lombok.

Aproveitando, vamos criar também uma classe chamada CarEvents, que terá o papel de representar dados que serão apresentados no stream de eventos que vamos criar. Mais adiante voltamos a esse assunto. 😉

 

E agora um repositório para nossas operações no banco de dados:

Note que nossa interface extends Reactive Mongo Repository está contida no módulo Reactive MongoDB que selecionamos no Spring Initializr. Esta interface já possui suporte para programação reativa.

Para fazer consulta em nossa entidade, nada mais justo que uma classe de serviço, que vou chamar de “FluxCarService”.

 

Em nossa classe temos um Flux (1:n), que será responsável por retornar todos os modelos de carros que temos no banco de dados Mono(1:1), que por sua vez deverá retornar um modelo de carro a partir de um ID de identificação. Temos também um Flux de CarEvents, que será utilizado em nosso endpoint para criar um stream de eventos de um determinado dado. Note que ele espera receber um ID e, então, no intervalo de um segundo irá disparar um evento do conteúdo daquele dado com o respectivo ID. Em seguida o método ZIP da classe Flux se encarrega de aguardar até que os dados (os eventos) estejam prontos e os envia em um response respeitando o intervalo definido.

Para fazer o input dos dados no banco vamos criar um componente com algumas informações para serem salvas, mas para isso faremos uso da classe Flux do Project Reactor para iniciarmos nosso processo, “nosso fluxo”. Você vai notar que nossa classe seguirá uma cadeia de chamadas (um fluxo) para realizar o input de nossos dados no banco. A ideia do reactive programming é que tudo seja baseado em eventos. E quem executa esses eventos? O framework. =) De fato, ele vai saber a melhor hora para disparar uma chamada de forma a otimizar nossos recursos.

Como gosto de carros, vou utilizar alguns modelos em nosso exemplo. Note o fluxo de ações que será executado ao iniciar nosso sistema.

 

Para não encher de informações nosso banco de dados sempre que a aplicação for reiniciada, o primeiro passo do fluxo é excluir os dados que estiverem no banco e assim que essa etapa estiver concluída ele avança inserindo e exibindo no console nossas informações.

Agora vamos criar alguns endpoins para realizar a consulta dos dados que acabamos de inserir. E para deixar bem visível a diferença entre a escrita de um código orientado a eventos (fluxos) e a forma pela qual escrevemos métodos atualmente, vamos primeiro ao desenvolvimento tradicional e depois vamos reescrevê-los utilizando a API de Streams do Project Reactor implementada pelo Spring Boot.

Tradicional:

 

Bem trivial certo? Então bora para os testes! Vamos garantir que nossos endpoins estão funcionando corretamente e no terminal vamos fazer algumas chamadas para validar nosso trabalho. Vou utilizar o famoso “CURL”.

E o resultado da nossa primeira chamada para o endpoint “/cars” é:

 

Muito bem, menos um…  (= Agora vamos realizar uma busca pelo ID… Copie qualquer ID do resultado da chamada acima para utilizarmos no próximo teste:

 

Checked! E finalmente nosso stream de eventos… vou utilizar na chamada o mesmo ID no exemplo anterior. Para chamar nosso endpoint repita a URI acima e coloque no final/events:

 

Muito bem… Temos então nosso event streams que é disparado a cada 1 segundo. Legal! Agora vamos voltar para o assunto deste artigo. Vamos programar utilizando recursos reativos, os nossos endpoints, para que eles trabalhem de forma assíncrona e o framework faça o gerenciamento das chamadas de forma que otimize os recursos do nosso “servidor” e garanta a melhor performance.

Informação importante: o SpringBoot não está utilizando o TomCat como container para nossa aplicação! Isso mesmo, por enquanto o TomCat não oferece suporte para programação assíncrona reativa no SpringBoot. A partir da versão 2.0.0 o container (web server) de aplicação default utlizado é o Netty.

 

Netty is an asynchronous event-driven network application framework  for rapid development of maintainable high performance protocol servers & clients.”

Até a alguns meses atrás para conseguirmos testar reactive programming no Spring Boot precisávamos configurar um HttpServer (do projeto netty reactor) na “unha”. O código era esse:

Mas aí veio o Spring Boot Team e facilita mais essa. Thanks, Spring Boot team!

Voltando aos nossos endpoins reativos. =)

Vamos criar um @component dentro da nossa classe principal do projeto, a mesma que tem o método Main, ok? Este componente terá um papel muito importante, ele será nosso “identificador de rotas” -> Route Handles.

Não se esqueça de comentar a annotation da nossa classe controller @RestController para não termos problemas. =)

Com nossas Router Handles prontas, podemos escrever um @Bean Router Functions que vai usar e abusar delas:

 

E seguem nossas funções, nossas rotas, nossos endpoints reativos e funcionais. Essa Router Function é equivalente ao @Controller que tínhamos escrito anteriormente.

Obs: O IntelliJ já possui um plugin para nos ajudar quando for necessário debugar streams, ok? O nome dele é Java Stream Debugger. E bora testar novamente. A URI das nossas rotas (“endpoints”) continuam as mesmas. Sempre que um request for feito um interceptor vai capturar a chamada e encaminhar para a rota correspondente para completar:

E os ganhos? Podem variar dependendo do equipamento que está sendo utilizado para testes e também dos recursos utilizados, mas nos testes que realizei com esses poucos registros, de cara temos um ganho na performance até o response da chamada. Provavelmente esse ganho seja mais visível em um sistema maior e com muito mais dados. Note o tempo do Avarage. D:

(/cars)

curl http://localhost:8080/cars | json_pp (trivial endpoints)

curl http://localhost:8080/cars | json_pp (reactive programming)

(/cars/{id})

curl http://localhost:8080/cars/fc01c8a8-7b39-4ef3-9e3c-59a3d0152593 (trivial endpoints)

curl http://localhost:8080/cars/fc01c8a8-7b39-4ef3-9e3c-59a3d0152593 (reactive programming)

O que acharam? Usar? Não usar? Depende, né? =) Agora basta esperarmos pela versão 2.0.0.RELEASE e sair codando. Voce pode baixar essa API no github. Tem alguma dúvida ou algo a dizer? Aproveite os campos abaixo. See you!

É desenvolvedor Java e quer fazer parte de um time fantástico? Clique aqui.