首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >多线程环境下使用Spring WebClient的正确方法

多线程环境下使用Spring WebClient的正确方法
EN

Stack Overflow用户
提问于 2018-03-04 12:25:38
回答 2查看 41.5K关注 0票数 45

关于Spring WebClient,我有一个问题

在我的应用程序中,我需要执行许多类似的API调用,有时我需要在调用中更改头(身份验证令牌)。因此,问题是,这两种选择有什么更好的:

  1. 通过使其成为private final 字段,为MyService.class的所有传入请求创建一个WebClient,类似于下面的代码:

WebClient webClient = WebClient.builder() .baseUrl("inf") .defaultHeader(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON_VALUE) .defaultHeader(HttpHeaders.ACCEPT,MediaType.APPLICATION_JSON_VALUE) .build();

这里出现了另一个问题: WebClient线程安全吗?(因为许多线程都使用服务)

  1. 为传入服务类的每个新请求创建新的WebClient。

我想提供最大的性能,并以正确的方式使用它,但我不知道WebClient在其中是如何工作的,以及它期望如何使用。

谢谢。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-03-05 09:52:10

关于WebClient的两个关键问题

  1. 它的HTTP资源(连接、缓存等)由底层库管理,由您可以在ClientHttpConnector上配置的WebClient引用。
  2. WebClient是不可变的

考虑到这一点,您应该尝试在应用程序中重用相同的ClientHttpConnector,因为这将共享连接池--这可以说是性能中最重要的事情。这意味着您应该尝试从同一个WebClient调用派生所有WebClient.create()实例。Spring通过为您创建和配置一个WebClient.Builder bean来帮助您实现这一点,您可以在应用程序中的任何地方插入该bean。

因为WebClient是不可变的,所以它是线程安全的。WebClient用于反应性环境,在这种环境中,没有任何东西绑定到特定的线程(这并不意味着不能在传统的Servlet应用程序中使用)。

如果您想改变发出请求的方式,有几种方法可以做到这一点:

在构建器阶段配置东西

代码语言:javascript
代码运行次数:0
运行
复制
WebClient baseClient = WebClient.create().baseUrl("https://example.org");

根据每个请求配置事物

代码语言:javascript
代码运行次数:0
运行
复制
Mono<ClientResponse> response = baseClient.get().uri("/resource")
                .header("token", "secret").exchange();

从现有的客户端实例中创建新的客户端实例

代码语言:javascript
代码运行次数:0
运行
复制
// 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();
票数 79
EN

Stack Overflow用户

发布于 2020-03-04 00:45:05

根据我的经验,如果您在无法控制的服务器上调用外部API,则根本不使用WebClient,或者在关闭池机制时使用它。连接池带来的任何性能收益都被内置在(默认的反应堆-netty)库中的假设大大高估了,这些假设会在一个API调用上造成随机错误,而另一个API调用则被远程主机突然终止,等等。在某些情况下,您甚至不知道错误发生在哪里,因为调用都是从共享工作线程发出的。

我犯了一个使用WebClient的错误,因为RestTemplate的文档说它在将来会被废弃。事后看来,我会使用常规的HttpClient或Apache,但是如果您和我一样并且已经用WebClient实现了,您可以通过创建您的WebClient来关闭池,如下所示:

代码语言:javascript
代码运行次数:0
运行
复制
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();
}

*创建一个单独的WebClient并不意味着WebClient将有一个单独的连接池。只需查看HttpClient.create的代码-它调用HttpResources.get()来获取全局资源。您可以手动提供池设置,但考虑到即使在默认设置下也会出现错误,我认为这不值得冒险。

票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49095366

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档