Aplicação reativa com Spring WebFlux que realiza operações assíncronas e não bloqueantes, processamento em tempo real e propagação de mudanças com Event Streaming, Server-Sent Events, utilizando Netty, R2DBC, Flyway e Sink
A aplicação reativa tem como característica não deixar os clients pendurados, todos os I/O são não bloqueantes, ela tem operações assíncronas e processamento em tempo real com propagação de mudanças
- Netty como servidor de aplicação
- R2DBC para banco de dados relacional, pois suporta operações assíncronas e não bloqueants
- Flyway para apoio ao ORM, pois no Spring Data JPA as anotações criam as tabelas e as colunas, mas no reativo não, tem q ser criado através de scripts com Flyway
- Sink para possibilitar a propagação de mudanças no banco de dados
A partir do momento que um microservice de pedido publica um evento de pedido-criado
e os microservices de estoque, pagamento, qualificacao e fulfilment consomem esse evento, eles estão sendo notificados em tempo real, isso é processamento em tempo real, o sistema está sendo notificado por mudanças de posição em tempo real
Não só com broker de eventos, utilizando o pattern Publishe/Subscribe, mas também é possível fazer processamento em tempo real com Server-Sent Events ou WebSockets, e principalmente com Arquitetura de Streaming de Eventos, quando queremos, por exemplo, um projeto de rastreamento de frota de caminhões em tempo real, onde 5 mil caminhões enviam coordenadas geográficas a cada 5 segundos e a transportadora pode consultar em qualquer momento a rota percorrida de qualquer caminhão
Na nossa aplicação, nós utilizamos Server-Sent Events para fazer o processamento em tempo real, o endpoint que lista os eventos por categoria por exemplo, é uma request que se mantem aberta, e ao ser cadastrado um novo evento para aquela categoria, o cliente que enviou a request, e está com ela aberta, recebe o novo evento cadastrado também
A classe Ingresso.java tem a informação da quantidade de ingressos disponíveis, e a classe Venda.java tem as informações ingressoId e total de ingressos que serão comprados
O endpoint que lista todos os ingressos vai se manter aberta, mostrando a quantidade de ingressos disponíveis, o endpoint de compra vai realizar o checkout recebendo id do ingresso, ele subtrai a quantidade de ingressos pela quantidade que será comprada, e atualiza a tabela ingresso
Por causa do Sink, o endpoint de ingressos vai atualizar a quantidade de ingresos disponíveis ou seja, conforme o cliente for comprando, o site vai mostrando quantos restam...
O endpoint que busca todos os eventos utiliza Event Streaming com Server-Sent Events, que faz uma comunicação unilateral em tempo real, na imagem abaixo podemos ver que o Postman abre uma conexão, vai recebendo os dados conforme vão chegando em um fluxo contínuo e fecha a conexão quando a busca finaliza
No paradigma reativo, a ideia é utilizar um modelo de programação assíncrono e não bloqueante, onde as operações de I/O (como chamadas de rede e acesso a banco de dados) não bloqueiam a execução das threads. Em vez disso, elas utilizam callbacks ou promessas para lidar com a conclusão dessas operações.
-
Escritas no Banco de Dados:
Em um ambiente reativo, as operações de escrita no banco de dados são feitas de forma assíncrona. Isso significa que, quando você faz uma operação de escrita, ela é iniciada e, em vez de bloquear a thread até que a operação termine, o controle é devolvido imediatamente. Quando a operação de escrita é concluída, um callback é acionado para lidar com o resultado. O uso de drivers reativos para bancos de dados, como o R2DBC, permite que essas operações sejam realizadas de forma não bloqueante.
-
Requisições para APIs Externas:
Similarmente, quando uma requisição é feita a uma API externa, a chamada é feita de forma assíncrona. O controle é devolvido imediatamente após a requisição ser enviada, e a resposta é tratada por meio de callbacks ou fluxos reativos quando chega. WebClient, por exemplo, é uma ferramenta em Spring WebFlux que facilita essas chamadas de forma não bloqueante. Mesmo que você esteja esperando a resposta para continuar o processamento (o que parece sincrônico), a diferença está em como os recursos do sistema são gerenciados. Em um modelo não bloqueante, o sistema pode continuar a processar outras tarefas enquanto aguarda a conclusão das operações de I/O, tornando-o mais eficiente em termos de uso de recursos.
Essas chamadas assíncronas são incríveis, principalmente porque a thread não fica bloqueada em uma conexão de rede, ela entra em um estado de espera sem consumir recursos, aguardando o callback.
Outras formas de fazer programação não bloqueante:
- Enviar diversas requests para N servidores diferentes ao mesmo tempo, em um processo sincrono por exemplo, a API iria fazer uma request, e aguardar o response para fazer outra request, e em um processo assincrono com programação não bloqueante, enquanto uma request é feita, outra já está sendo feita também, mesmo sem a resposta da primeira, exemplo prático: calcula_folha_pagamento.go
- Melhorando uma aplicação com Single-Thread Server
Nem sempre a API reativa conveniente. Ela é ideal quando precisamos trabalhar com alta concorrência, escalabilidade, baixa latência, performance, fluxo assíncrono, notificações em tempo real, controle de fluxo, e assim por diante.
Por outro lado, para aplicações mais simples, de baixo tráfego, que exigem compatibilidade ou simplicidade para debug, a API Servlet pode ser mais adequada.
Cada caso é único. Como pessoas desenvolvedoras, precisamos analisar o tipo de aplicação para decidir o que atende melhor. No caso do CodeChella, precisávamos de controle em tempo real para vendas de ingressos, então optamos pela API reativa.