Spring 用于同步请求的 RestTemplate 与 WebClient

Spring RestTemplate vs WebClient for sync requests

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

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

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

提前致谢!

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

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

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

回答您的问题:

  1. 请不要那样做。通过这样做,您将 Spring Webflux 的所有好处都扔进了垃圾桶。您正在复制 non-reactive 堆栈行为。
  2. 实际上是的。线程将被阻塞,等待您调用的服务的响应。
  3. 我会说它会很相似,但如果没有任何好处,您的代码将更难阅读。

底线是:为了充分利用 Spring Webflux 的反应堆栈,您需要有一个完全反应的代码库。即使是基础设施也必须支持反应行为,最简单的例子就是数据库。

此外,您混淆了异步行为和反应行为。 Spring Webflux 本质上是反应性的,这给 table 带来了其他好处(看看 https://www.baeldung.com/spring-mvc-async-vs-webflux)。

既然好像有什么误解,我会尽力回答问题。

How safe is it to use .block() and is it ok in general to block threads in WebClient?

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

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

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

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

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

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

你完全反应了吗?不,你是不是像以前一样是一个阻塞的网络服务器,是的。 RestTemplate 很可能不会更糟吗?从维护的角度来看,您是否比以前更好,是的,因为 spring 已经正式退出,不会再对 RestTemplate.

进行任何更新

Is the mechanics behind blocking calls with WebClient similar to what RestTemplate does?

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

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

Is there a possibility that the performance would be worse than in case I just use RestTemplate?

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

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

由于 Netty 是事件驱动的,运行将它作为一个 服务器,每个请求一个线程可能会损害性能,因为它没有针对那种类型的工作,但另一方面,当你 运行 它完全由事件驱动时,它会发光。

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

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