Programação reativa e Spring WebFlux
void main() => runApp(
PessoaApp(
nome: 'Cácio Costa',
empresa: 'Alura',
cargo: 'Instrutor e pagodeiro nas horas vagas',
),
);

1
Considerações sobre programação reativa
2
Projeto Reactor
3
Spring WebFlux
4
Mão na massa
5
Considerações
finais
Seria legal se você soubesse ou tivesse noção em...
-
... Spring Boot e o ecossistema do Spring (módulos populares);
-
... API REST;
-
... Docker e Mongo DB.

Consideração sobre
programação reativa

O que é ser reativo?

Princípios básicos

Reativo X Imperativo

Mudando o modo de pensar


Imperativo ➡
Reativo ➡
Fluxo de dados reativo

Operações em paralelo

Projeto Reactor

Core types

Mono e Flux
public class ExemploDeCoreTypes {
/**
* Mono representa um fluxo de 0..1 elementos
*/
public Mono<String> getMensagem() {
return Mono.just("Olá, mundo reativo!");
}
/**
* Flux representa um fluxo de 0..N elementos
*/
public Flux<Integer> getFlux() {
return Flux.range(1, 5)
.delayElements(Duration.ofSeconds(1));
}
}Operadores reativos comuns

Map, FlatMap e Filter
public class OperadoresReativos {
public Flux<String> processaDados() {
return Flux.just(1, 2, 3, 4, 5)
// `map` transforma um valor em outro
.map(i -> i * 2)
// `filter` remove elementos que não passam no predicado
.filter(i -> i > 5)
// `flatMap` mapeia e "achata" fluxos aninhados.
.flatMap(i -> Flux.just("Número: " + i, "Potência: " + (i * i)));
}
}Operadores reativos comuns

Zip
@RestController
public class OperadoresReativos {
// Atributos e construtor
@GetMapping(value = "/combined-data", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<CombinedData> getCombinedData() {
return Flux.zip(
serviceA.getData(),
serviceB.getData(),
(dataA, dataB) -> new CombinedData(dataA, dataB)
);
}
}Backpressure

Lidando com altas cargas
@RestController
public class OperadoresReativos {
// atributos e construtor
@GetMapping(value = "/stream-data-drop", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Data> fluxoComDrop() {
return dataSource.getData()
.limitRate(100) // Controle de backpressure
.onBackpressureDrop(); // Descarta elementos que o cliente não conseguir processar
}
@GetMapping(value = "/stream-data-buffer", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Data> fluxoComBuffer() {
return dataSource.getData()
.limitRate(100) // Controle de backpressure
.onBackpressureBuffer(10); // Cria um buffer com 10 elementos
}
}Schedulers

Concorrência simplificada
@RestController
public class SchedulersController {
// atributos e construtor
@GetMapping("/processo-paralelo")
public Flux<Result> processoParalelo() {
return Flux.range(1, 100)
.parallel()
.runOn(Schedulers.parallel())
.map(this::processaItem)
.sequential();
}
private void processaItem(Integer numero) {
// processamento pesado...
}
}Immediate
Executa processamento na thread atual.
Single
Executa em uma única thread dedicada.
Parallel
Executa em um pool de threads paralelo.
Schedulers

Tipos
Bounded Elastic
Cria threads sob demanda com um limite máximo.
Schedulers

Combinando Schedulers
@RestController
public class SchedulersController {
// atributos e construtor
@GetMapping("/tarefa-intensa")
public Flux<Result> tarefaIntensa() {
return Flux.range(1, 100)
.flatMap(i -> Mono.fromCallable(() -> executaComputacaoPesada(i))
.subscribeOn(Schedulers.boundedElastic()))
.publishOn(Schedulers.parallel());
}
private Result executaComputacaoPesada(int input) {
// Simulação de uma tarefa pesada
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return new Result(input, input * input);
}
}Tratamento de erro

onErrorReturn
Flux.just("A", "B")
.concatWith(Flux.error(new RuntimeException("Erro"))) // concatena fluxo de erro
.onErrorReturn("Default") // retorna valor padrão em caso de erro
.subscribe(
valor -> System.out.println("Recebeu: " + valor),
erro -> System.err.println("Erro: " + erro)
);
// SAÍDA
Recebeu: A
Recebeu: B
Recebeu: DefaultTratamento de erro

onErrorResume
Flux.just("A", "B")
.concatWith(Flux.error(new RuntimeException("Erro")))
.onErrorResume(e -> Flux.just("Fallback1", "Fallback2")) // altera para fluxo alternativo
.subscribe(
valor -> System.out.println("Recebeu: " + valor),
erro -> System.err.println("Erro: " + erro)
);
// SAÍDA
Recebeu: A
Recebeu: B
Recebeu: Fallback1
Recebeu: Fallback2Tratamento de erro

onErrorContinue
Flux.range(1, 5)
.map(i -> {
if (i == 3) {
throw new RuntimeException("Erro");
}
return i;
})
.onErrorContinue((erro, item) ->
System.err.println("Erro aconteceu no item " + item)
)
.subscribe(
valor -> System.out.println("Recebeu: " + valor),
erro -> System.err.println("Erro: " + erro)
);
// SAÍDA
Received: 1
Received: 2
Received: 4
Received: 5
Erro aconteceu no item 3Tratamento de erro

Tratando erros específicos
Flux.just("A", "B")
.concatWith(Flux.error(new RuntimeException("Erro")))
.onErrorResume(IOException.class, e -> Flux.just("IO Fallback"))
.onErrorResume(RuntimeException.class, e -> Flux.just("Runtime Fallback"))
.subscribe(
valor -> System.out.println("Recebeu: " + valor),
erro -> System.err.println("Erro: " + erro)
);
// SAÍDA
Recebeu: A
Recebeu: B
Recebeu: Runtime FallbackSpring WebFlux

Spring WebFlux VS Spring MVC

Diferenças

Mão na massa...

Considerações finais

Obrigado
(def recursos {:linkedin "/cacio-costa/"
:email "cacio.costa@alura.com.br"
:repo "https://github.com/cacio-costa/exemplo-spring-webflux"
:ig-grupo-qdqa "@grupoqdqa"})Programação reativa com Spring WebFlux
By cacio-costa
Programação reativa com Spring WebFlux
- 16