【问题标题】:Spring RestTemplate vs WebClient for sync requests用于同步请求的 Spring RestTemplate 与 WebClient
【发布时间】:2023-02-05 20:30:00
【问题描述】:

抱歉,如果之前有人问过这个问题,但我没有找到匹配的问题。

我有一个应用程序可以对其他服务执行 api 调用。我正在考虑按照 Spring 的建议使用 WebClient 而不是 RestTemplate。我正在执行专门的同步调用。我知道 WebClient 在设计时考虑了反应式方法,但理论上仅将 WebClient 用于阻止调用是否可以?我担心我必须在每次调用时调用 .block() 才能获取数据。所以我的问题是:

  1. 使用.block() 有多安全?在 WebClient 中阻塞线程通常是否正常?
  2. 使用 WebClient 阻止调用背后的机制是否类似于 RestTemplate 所做的?
  3. 性能是否有可能比我只使用 RestTemplate 时更差?

    提前致谢!

【问题讨论】:

    标签: java spring spring-webflux resttemplate spring-webclient


    【解决方案1】:

    由于似乎存在误解,我将尽我所知尝试回答问题。

    使用 .block() 的安全性如何?通常可以在 WebClient 中阻塞线程吗?

    阻塞总是安全的但天气与否会影响性能是另一回事。当一个请求进来时,它会被分配一个线程。当我们使用 RestTemplate 发出请求时,同一个线程将执行外部请求,而 RestTemplate 将在后台阻塞该线程以等待响应。

    这绝不是线程的有效使用,但它完全安全的,这就是大多数 Web 服务器在过去 20 年中的工作方式。

    当在非反应性应用程序中使用 WebClient 并且您阻止了 Mono<T>(您实际上会这样做)时,框架将首先检查该线程是否是您被允许阻止的线程类型(不是 nio- thread) 然后它使用 CountDownLatch 暂停/阻塞 CountDownLatch#await 的调用线程,直到第一个 onNext/onComplete/onError 信号到达。这是完全没问题在阻塞应用程序中。你可以找到相关代码here

    当您将 WebClient 添加到类路径时,您将自动获得 netty 作为底层服务器,这可能是个好消息。如果你想改变它,那么你需要明确说明。

    此外,建议如果您执行多个请求,那么在诉诸 block 之前,您应该尽可能多地链接响应式调用。

    如果你想转移到反应式应用程序,那么这是一种慢慢转移应用程序的好方法,慢慢地做越来越多的反应性事情,然后调用 block 返回到常规的世界。

    你完全反应了吗?不,你是不是像以前一样是一个阻塞的网络服务器,是的。 RestTemplate 很可能不会更糟吗?你比以前好了吗,从维护的角度来看,是的,因为春天已经正式结束,所以不会再对RestTemplate进行任何更新。

    使用 WebClient 阻止调用背后的机制是否类似于 RestTemplate 所做的?

    这很难说,因为 RestTemplate 主要只是底层服务器实现提供的 HttpClient 的包装。

    阻塞的编写方式可能不同,但它们最终所做的很可能是相同的。 Mono<T> 使用 CountDownLatch 重复调用 getCount 块,然后在块之间调用闩锁 await 直到响应返回。我还没有查看 RestTemplate 包装的不同 HttpClients,您需要仔细阅读它们中的每一个(tomcat、jetty、undertow 等)

    性能是否有可能比我只使用 RestTemplate 时更差?

    这是极其很难说,因为性能不是黑白的。这完全取决于硬件、要完成的工作类型、代码的编写方式、线程池大小、操作系统等。

    Netty 是一个完全事件驱动的服务器,它开始成为事实上的Java 社区中的 Web 服务器标准。 Undertow decided to switch out their entire core to the netty core,因为它非常好,而且更容易维护。

    由于 Netty 是事件驱动的,因此将其作为老的每个请求一个线程的服务器可能会损害性能,因为它没有针对此类工作进行优化,但另一方面,当您完全运行它时,它会以事件驱动的方式运行,它会大放异彩。

    回答这个问题的唯一方法是做自己的基准测试,我们无法为您回答。

    如果您想了解更多有关 netty 实际工作原理的信息,建议您阅读 Netty in Action 这本书,这本书不是免费的,但非常适合理解 Netty 及其 async 线程模型的内部工作原理。

    【讨论】:

    • 谢谢,我认为这是我期待的那种答案。我会赞成它,但没有足够的声誉:(不过你很有帮助,我很感激你花在上面的时间!
    • 只是一个小的更正:await 链接转到测试实用程序方法。 block 不是这样实现的。真正的实现使用CountDownLatch
    • 是的,更新了链接
    • “在 CountDownLatch#await 处暂停,直到没有剩余线程”-“没有剩余线程”是什么意思?它等到第一个下一个/完成/错误事件。
    • 我知道 CountDownLatch 是如何工作的。这就是我问这个问题的原因,因为我不清楚你指的是什么线程。我发现这有点令人困惑。我会添加我的编辑。谢谢你给我的机会。否则一个很好的答案!
    【解决方案2】:

    在我们的应用程序中,我们从 RestTemplate 迁移到 WebClient 没有任何问题,.block() 工作正常

    Response response = this.webClient
            .post()
            .uri(uri)
            .body(fromValue)
            .retrieve()
            .bodyToMono(Response.class)
            .timeout(Duration.ofMillis(timeoutMillis))
            .block();
    

    这和 RestTemplate 做的一样,它以同步的方式发送请求,我们已经在 PROD 中工作了几个月没有任何问题

    【讨论】:

    • 感谢您的回复!所以我想一般来说在 WebClient 中阻塞线程是可以的,没什么好害怕的,除​​非它是在反应式应用程序中完成的?
    • 是的,我不知道在反应式应用程序中 .block() 会发生什么。对于响应式应用,我们使用 Mono<> 响应
    • 只是想知道 - 如果例如长时间没有响应并且我们使用没有超时的请求,我们是否会耗尽 Web 客户端的线程池?使用超时来终止请求线程真的可以吗?也许我错了,我正在与 CompletableFuture 和另一个异步框架(如 Play!阻止或超时请求真的是个坏主意并且在概念上是错误的,因为它会导致性能下降并且在使用反应式客户端时根本没有意义?
    • 请不要这样做。与旧的 RestTemplate 相比,这对您的应用程序没有任何价值。
    • @tracer_13 这还不错,但我会坚持使用 RestTemplate,直到它在任何时候被弃用。在非反应性应用程序中使用 WebClient 几乎没有什么价值(取决于你如何使用它,但正如这个答案中所描述的那样,它没有额外的价值)并且使它更难阅读。在反应式应用程序中使用 block() 更糟糕,这就是我认为你正在做的。
    【解决方案3】:

    值得补充的是,如果您想在仅使用spring-boot-starter-webflux依赖项的同时以阻塞方式使用webClient,将抛出类似block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3的异常,因此为了以阻塞方式使用Webclient,您需要配置通过添加 spring-boot-starter-web 的 spring MVC 应用程序,如文档Web Environment 中所述:

    SpringApplication 尝试创建正确类型的 ApplicationContext 代表你。用于确定的算法 WebApplicationType 如下:

    如果存在 Spring MVC,则 使用 AnnotationConfigServletWebServerApplicationContext

    如果 Spring MVC 不存在而 Spring WebFlux 存在,则 使用 AnnotationConfigReactiveWebServerApplicationContext

    否则,使用 AnnotationConfigApplicationContext

    这意味着如果您使用的是 Spring MVC 和来自 Spring WebFlux 在同一个应用程序中,Spring MVC 将被使用 默认。您可以通过调用轻松覆盖它 setWebApplicationType(WebApplicationType)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-07
      • 2020-05-04
      • 1970-01-01
      • 2020-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多