J'ai une question concernant Spring WebClient
Dans mon application, je dois faire de nombreux appels API similaires, parfois j'ai besoin de changer les en-têtes dans les appels (jeton d'authentification). La question se pose donc, quelle serait la meilleure des deux options:
Pour créer un WebClient pour toutes les demandes entrantes à MyService.class, en le faisant private final
champ, comme le code ci-dessous:
private final WebClient webClient = WebClient.builder()
.baseUrl("https://another_Host.com/api/get_inf")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.build();
Ici se pose une autre question: WebClient est-il sûr pour les threads? (car le service est utilisé par de nombreux threads)
Je veux fournir des performances maximales et les utiliser correctement, mais je ne sais pas comment WebClient fonctionne à l'intérieur, ni comment il s'attend à être utilisé.
Je vous remercie.
Deux choses clés ici à propos de WebClient
:
ClientHttpConnector
que vous pouvez configurer sur le WebClient
WebClient
est immuableDans cet esprit, vous devriez essayer de réutiliser le même ClientHttpConnector
dans votre application, car cela partagera le pool de connexions - c'est sans doute la chose la plus importante pour les performances. Cela signifie que vous devez essayer de dériver toutes les instances WebClient
du même appel WebClient.create()
. Spring Boot vous y aide en créant et en configurant pour vous un WebClient.Builder
bean que vous pouvez injecter n'importe où dans votre application.
Parce que WebClient
est immuable, il est thread-safe. WebClient
est destiné à être utilisé dans un environnement réactif, où rien n'est lié à un thread particulier (cela ne signifie pas que vous ne pouvez pas l'utiliser dans une application Servlet traditionnelle).
Si vous souhaitez modifier la façon dont les demandes sont faites, il existe plusieurs façons d'y parvenir:
WebClient baseClient = WebClient.create().baseUrl("https://example.org");
Mono<ClientResponse> response = baseClient.get().uri("/resource")
.header("token", "secret").exchange();
// mutate() will *copy* the builder state and create a new one out of it
WebClient authClient = baseClient.mutate()
.defaultHeaders(headers -> {headers.add("token", "secret");})
.build();
Ne devrait-il pas être:
WebClient wc = WebClient
.builder()
.baseUrl(SERER_Origin)
.build();
Au lieu
WebClient wc = WebClient.create().baseUrl("https://example.org");
?
D'après mon expérience, si vous appelez une API externe sur un serveur sur lequel vous n'avez aucun contrôle, n'utilisez pas du tout WebClient ou utilisez-le avec le mécanisme de pooling désactivé. Tous les gains de performances du pool de connexions sont largement compensés par les hypothèses intégrées à la bibliothèque (par défaut réacteur-netty) qui provoqueront des erreurs aléatoires sur un appel d'API lorsqu'un autre a été brutalement interrompu par l'hôte distant, etc. Dans certains cas, vous ne '' Je ne sais même pas où l'erreur s'est produite car les appels sont tous effectués à partir d'un thread de travail partagé.
J'ai fait l'erreur d'utiliser WebClient parce que le doc pour RestTemplate a dit qu'il serait obsolète à l'avenir. Avec le recul, j'irais avec HttpClient normal ou Apache Commons HttpClient, mais si vous êtes comme moi et déjà implémenté avec WebClient, vous pouvez désactiver le regroupement en créant votre WebClient comme suit:
private WebClient createWebClient(int timeout) {
TcpClient tcpClient = TcpClient.newConnection();
HttpClient httpClient = HttpClient.from(tcpClient)
.tcpConfiguration(client -> client.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, timeout * 1000)
.doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(timeout))));
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
}
*** La création d'un WebClient distinct ne signifie pas que WebClient aura un pool de connexions distinct. Regardez simplement le code de HttpClient.create - il appelle HttpResources.get () pour obtenir les ressources globales. Vous pouvez fournir les paramètres du pool manuellement, mais compte tenu des erreurs qui se produisent même avec la configuration par défaut, je ne pense pas que cela en vaille la peine.