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: Default

Tratamento 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: Fallback2

Tratamento 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 3

Tratamento 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 Fallback

Spring 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