在现代Spring中应该如何同步http请求?

eis*_*eis 8 java spring synchronous reactive spring-webclient

长期以来,Spring 一直推荐使用RestTemplate来同步 http 请求。然而,现在的文档说:

注意:从 5.0 开始,此类处于维护模式,仅接受较小的更改请求和错误。请考虑使用 org.springframework.web.reactive.client.WebClient ,它具有更现代的 API 并支持同步、异步和流场景。

但我还没有看到如何建议使用WebClient进行同步场景。文档中有这样的内容:

WebClient 可以通过在结果结束时阻塞来以同步方式使用

我已经看到一些代码库到处都使用 .block() 。然而,我的问题是,凭借一些反应式框架的经验,我逐渐明白阻止反应式调用是一种代码味道,实际上应该只在测试中使用。例如这个页面

有时,您只能将部分代码迁移为响应式,并且需要在更多命令式代码中重用响应式序列。

因此,如果您需要阻塞直到 Mono 的值可用,请使用 Mono#block() 方法。如果 onError 事件被触发,它将抛出异常。

请注意,您应该通过尽可能支持端到端的反应式代码来避免这种情况。您必须在其他反应式代码中间不惜一切代价避免这种情况,因为这有可能锁定整个反应式管道。

那么我是否错过了一些可以避免 block() 但允许您进行同步调用的东西,或者是否在任何地方都使用 block() 真的是这样?

或者 WebClient API 的意图是暗示人们不应该再在代码库中的任何地方进行阻塞?由于 WebClient 似乎是 Spring 提供的未来 http 调用的唯一替代方案,因此未来在整个代码库中使用非阻塞调用并更改代码库的其余部分以适应这一点是唯一可行的选择吗?

这里有一个相关的问题,但它只关注发生的异常,而我有兴趣听到一般的方法应该是什么。

Pan*_*kos 1

首先根据WebClient Java文档

\n
\n

公共接口 WebClient\n用于执行 HTTP 请求的非阻塞、反应式客户端,通过底层 HTTP 客户端库(例如 Reactor Netty)公开流畅的反应式API。使用静态工厂方法 create() 或 create(String)\n 或 builder() 来准备实例。

\n
\n

所以 webClient 并不是为了以某种方式阻塞而创建的。

\n

然而,webClient 返回的响应可以是 type 类型<T> reactor.core.publisher.Flux<T>,有时也可以是 type 类型<T> reactor.core.publisher.Mono<T>。Reactor项目中的Flux和Mono是具有阻塞方法的。来自 WebClient 的 ResponseSpec。

\n

WebClient 被设计为响应式客户端。

\n

正如您可能从其他语言的其他反应式库(例如JavaScript 的 RxJs)中看到的那样,反应式编程通常基于函数式编程。

\n

项目Flux中发生的事情是它们允许您进行同步执行,而不需要函数式编程。Monoreactorblock()

\n

这是我觉得很有趣的一篇文章的一部分

\n
\n

提取者:来自黑暗面的订阅者

\n

还有另一种订阅序列的方法,即调用 Mono.block() 或 Mono.toFuture() 或 Flux.toStream() (这些是“提取器”方法\xe2\x80\x89\xe2 \x80\x94\xe2\x80\x89它们让你摆脱反应式类型,进入一种不太灵活的、\n阻塞抽象)。Flux 还具有从 Flux 转换为 Mono 的转换器collectList() 和\ncollectMap()。他们实际上并不订阅该序列,但他们确实放弃了您在单个项目级别上对订阅可能拥有的任何控制权。

\n

警告 一个好的经验法则是“永远不要调用提取器”。有一些例外(否则这些方法将不存在)。一个值得注意的例外是在测试中,因为能够阻止以允许结果累积非常有用。这些方法可以作为从响应式到阻塞式的桥梁;如果您需要适应旧版 API,例如 Spring MVC。当你调用 Mono.block() 时,你就放弃了响应式流的所有好处

\n
\n

那么不使用操作可以进行同步编程block()吗?

\n

是的, 您可以,但是您必须考虑应用程序的函数式编程\n。

\n

例子

\n
   public void doSomething1( ) {\n      webClientCall_1....subscribe( response1 -> {\n\n          ...do something else ...\n          webClientCall_2....subscribe( response2 -> {\n              ...do something else more with response1 and response2 available here...\n            });\n       });\n   }\n
Run Code Online (Sandbox Code Playgroud)\n

这就是所谓的订阅回调地狱。您可以使用.block()方法来避免它,但正如所提供的文章所提到的,它们抛弃了该库的反应性本质

\n