web-dev-qa-db-fra.com

Spring Boot: comment utiliser WebClient au lieu de RestTemplate pour effectuer des appels non bloquants et asynchrones

J'ai un projet Springboot qui utilise Springboot Resttemplate. Nous avons migré vers springboot 2.0.1 à partir de 1.5.3 et nous essayons de faire les autres appels de manière asynchrone à l'aide de WebClient. Nous avons utilisé pour traiter la chaîne reçue à l'aide de Resttemplate comme indiqué ci-dessous. Mais WebClient ne renvoie que des données en Mono ou Flux. Comment puis-je obtenir les données sous forme de chaîne. Vous avez déjà essayé la méthode block (), mais elle effectue des appels asynchrones.

@Retryable(maxAttempts = 4, value = Java.net.ConnectException.class,
           backoff = @Backoff(delay = 3000, multiplier = 2))
public Mono<String> getResponse(String url) {
    return webClient.get().uri(urlForCurrent).accept(APPLICATION_JSON)
                    .retrieve()
                    .bodyToMono(String.class);
}

Flux de données actuel avec RestTemplate

  1. Le contrôleur reçoit l'appel client
  2. le fournisseur obtient les données au format String
  3. Le fournisseur traite la chaîne
  4. Les données sont transmises au contrôleur

Controller.Java

@RequestMapping(value = traffic/, method = RequestMethod.GET,
                produces = MediaType.APPLICATION_JSON_VALUE)
public String getTraffic(@RequestParam("place") String location) throws InterruptedException, ExecutionException {
    String trafficJSON = Provider.getTrafficJSON(location)
    return trafficJSON;
}

Provider.Java

public String getTrafficJSON(String location) {
    String url = ----;

    ResponseEntity<String> response = dataFetcher.getResponse(url);

    /// RESPONSEBODY IS READ AS STRING AND IT NEEDS TO BE PROCESSED
    if (null != response {
        return parser.transformJSON(response.getBody(), params);
    }

    return null;
}

DataFetcher.Java

@Retryable(maxAttempts = 4,
           value = Java.net.ConnectException.class,
           backoff = @Backoff(delay = 3000, multiplier = 2))
public ResponseEntity<String> getResponse(String url) {
    /* ----------------------- */
    return getRestTemplate().getForEntity(urlForCurrent, String.class);
}
3
Abhi

La première chose à comprendre est que si vous avez besoin d'appeler .block(), vous pouvez tout aussi bien rester avec RestTemplate, l'utilisation de WebClient ne vous rapportera rien.

Vous devez commencer à penser en termes réactifs si vous souhaitez gagner à utiliser WebClient. Un processus réactif n'est en réalité qu'une séquence d'étapes, l'entrée de chaque étape étant la sortie de l'étape précédente. Lorsqu'une demande arrive, votre code crée la séquence d'étapes et renvoie immédiatement la libération du thread http. Le cadre utilise ensuite un pool de threads de travail pour exécuter chaque étape lorsque l'entrée de l'étape précédente devient disponible.

L'avantage est un énorme gain de capacité à accepter des demandes concurrentes au moindre coût d'avoir à repenser la façon dont vous écrivez le code. Votre application n'aura besoin que d'un très petit pool de threads http et d'un autre très petit pool de threads de travail.

Lorsque votre méthode de contrôleur renvoie un Mono ou Flux, vous avez raison et vous n'aurez pas besoin d'appeler block().

Quelque chose comme ça dans sa forme la plus simple:

@GetMapping(value = "endpoint", produces = MediaType.TEXT_PLAIN_VALUE)
@ResponseBody
@ResponseStatus(OK)
public Mono<String> controllerMethod() {

    final UriComponentsBuilder builder =
            UriComponentsBuilder.fromHttpUrl("http://base.url/" + "endpoint")
                    .queryParam("param1", "value");

    return webClient
            .get()
            .uri(builder.build().encode().toUri())
            .accept(APPLICATION_JSON_UTF8)
            .retrieve()
            .bodyToMono(String.class)
            .retry(4)
            .doOnError(e -> LOG.error("Boom!", e))
            .map(s -> {

                // This is your transformation step. 
                // Map is synchronous so will run in the thread that processed the response. 
                // Alternatively use flatMap (asynchronous) if the step will be long running. 
                // For example, if it needs to make a call out to the database to do the transformation.

                return s.toLowerCase();
            });
}

Passer à la pensée réactive est un changement de paradigme assez important, mais qui en vaut la peine. Accrochez-vous, ce n'est vraiment pas si difficile une fois que vous pouvez comprendre que vous n'avez aucun code de blocage du tout dans votre application. Construisez les marches et retournez-les. Laissez ensuite le framework gérer les exécutions des étapes.

Heureux de fournir plus de conseils si tout cela n'est pas clair.

N'oubliez pas de vous amuser :)

1
Captain