web-dev-qa-db-fra.com

Comment exécuter des appels de blocage dans une application Web Spring Webflux / Reactor Netty

Dans mon cas d'utilisation où j'ai un microservice Spring Webflux avec Reactor Netty, j'ai les dépendances suivantes:

  • org.springframework.boot.spring-boot-starter-webflux (2.0.1.RELEASE)
  • org.springframework.boot.spring-boot-starter-data-mongodb-reactive (2.0.1.RELEASE)
  • org.projectreactor.reactor-spring (1.0.1.RELEASE)

Pour un cas très spécifique, j'ai besoin de récupérer certaines informations de ma base de données Mongo, et de les traiter en paramètres de requête envoyés avec mon WebClient réactif. Comme le WebClient ni le UriComponentsBuilder accepte un éditeur (Mono/Flux), j'ai utilisé un appel #block() pour recevoir les résultats.

Depuis reactor-core (Version 0.7.6.RELEASE) qui a été inclus dans la dernière spring-boot-dependencies (Version 2.0.1.RELEASE) il n'est plus possible d'utiliser: block()/blockFirst()/blockLast() are blocking, which is not supported in thread xxx , voir -> https://github.com/reactor/reactor-netty/issues/312

Mon extrait de code:

public Mono<FooBar> getFooBar(Foo foo) {
    MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
    parameters.add("size", foo.getSize());
    parameters.addAll("bars", barReactiveCrudRepository.findAllByIdentifierIn(foo.getBarIdentifiers()) // This obviously returns a Flux
        .map(Bar::toString)
        .collectList()
        .block());

    String url = UriComponentsBuilder.fromHttpUrl("https://base-url/")
        .port(8081)
        .path("/foo-bar")
        .queryParams(parameters)
        .build()
        .toString();

    return webClient.get()
        .uri(url)
        .retrieve()
        .bodyToMono(FooBar.class);
}

Cela a fonctionné avec spring-boot Version 2.0.0.RELEASE, mais depuis la mise à niveau vers la version 2.0.1.RELEASE et donc la mise à niveau de reactor-core Vers la version 0.7.6.RELEASE, il n'est plus autorisé .

La seule vraie solution que je vois est d'inclure également un référentiel bloc (non réactif)/client mongo, mais je ne sais pas si cela est encouragé. Aucune suggestion?

6
Dennis Nijssen

WebClient n'accepte pas un type Publisher pour son URL de requête, mais rien ne vous empêche de faire ce qui suit:

public Mono<FooBar> getFooBar(Foo foo) {

    Mono<List<String>> bars = barReactiveCrudRepository
        .findAllByIdentifierIn(foo.getBarIdentifiers())
        .map(Bar::toString)
        .collectList();

    Mono<FooBar> foobar = bars.flatMap(b -> {

        MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
        parameters.add("size", foo.getSize());
        parameters.addAll("bars", b);

        String url = UriComponentsBuilder.fromHttpUrl("https://base-url/")
            .port(8081)
            .path("/foo-bar")
            .queryParams(parameters)
            .build()
            .toString();

        return webClient.get()
            .uri(url)
            .retrieve()
            .bodyToMono(FooBar.class);
    });
    return foobar;         
}

Si quoi que ce soit, cette nouvelle inspection du cœur du réacteur vous a évité de planter toute votre application avec cet appel de blocage au milieu d'un gestionnaire WebFlux.

4
Brian Clozel