void main() => runApp(
PessoaApp(
nome: 'Cácio Costa',
empresa: 'Alura',
cargo: 'Instrutor e pagodeiro nas horas vagas',
),
);Considerações sobre programação reativa
Projeto Reactor
Spring WebFlux
Mão na massa
Considerações
finais
... Spring Boot e o ecossistema do Spring (módulos populares);
... API REST;
... Docker e Mongo DB.
Princípios básicos
Mudando o modo de pensar
Imperativo ➡
Reativo ➡
Operações em paralelo
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));
}
}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)));
}
}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)
);
}
}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
}
}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...
}
}ImmediateExecuta processamento na thread atual.
SingleExecuta em uma única thread dedicada.
ParallelExecuta em um pool de threads paralelo.
Tipos
Bounded ElasticCria threads sob demanda com um limite máximo.
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);
}
}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: DefaultonErrorResume
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: Fallback2onErrorContinue
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 3Tratando 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 FallbackDiferenças
(def recursos {:linkedin "/cacio-costa/"
:email "cacio.costa@alura.com.br"
:repo "https://github.com/cacio-costa/exemplo-spring-webflux"
:ig-grupo-qdqa "@grupoqdqa"})