我可以使用从 Spring5 的 WebClient 返回的 Flux 的 block() 方法吗?



我创建了Spring Boot 2.0演示应用程序,其中包含两个使用WebClient进行通信的应用程序。而且我很痛苦,当我从WebClient的响应中使用Flux的block()方法时,他们经常停止通信。出于某些原因,我想使用列表而不是 Flux。

服务器端应用程序是这样的。它只返回 Flux 对象。

@GetMapping
public Flux<Item> findAll() {
return Flux.fromIterable(items);
}

客户端(或BFF端)应用程序就是这样的。我从服务器获取 Flux 并通过调用 block() 方法将其转换为 List。

@GetMapping
public List<Item> findBlock() {
return webClient.get()
.retrieve()
.bodyToFlux(Item.class)
.collectList()
.block(Duration.ofSeconds(10L));
}

虽然一开始效果很好,但 findBlock() 在多次访问后不会响应和超时。当我修改findBlock()方法以返回Flux删除collectList()和block()时,它运行良好。然后我假设block()方法会导致这个问题。
而且,当我修改 findAll() 方法以返回 List 时,没有任何变化。

整个示例应用程序的源代码在这里。
https://github.com/cero-t/webclient-example

"资源"是服务器应用程序,"前端"是客户端应用程序。运行这两个应用程序后,当我访问 localhost:8080 时它运行良好,我可以随时重新加载,但是当我访问 localhost:8080/block 时,它似乎运行良好,但多次重新加载后它不会响应。


顺便说一下,当我将"spring-boot-starter-web"依赖项添加到"前端"应用程序(而不是资源应用程序)的pom.xml时,这意味着我使用tomcat,这个问题永远不会发生。这个问题是由Netty服务器引起的吗?

任何指导将不胜感激。

首先,让我指出,仅当items已从内存中获取时才建议使用Flux.fromIterable(items),不涉及 I/O。否则,您可能会使用阻塞 API 来获取它 - 这可能会破坏您的反应式应用程序。在这种情况下,这是一个内存中的列表,所以没问题。请注意,您也可以转到Flux.just(item1, item2, item3).

使用以下方法最有效:

@GetMapping("/")
public Flux<Item> findFlux() {
return webClient.get()
.retrieve()
.bodyToFlux(Item.class);
}

Item实例将以非常有效的方式即时读取/写入、解码/编码。

另一方面,这不是首选方式:

@GetMapping("/block")
public List<Item> findBlock() {
return webClient.get()
.retrieve()
.bodyToFlux(Item.class)
.collectList()
.block(Duration.ofSeconds(10L));
}

在这种情况下,前端应用程序在内存中缓冲整个项目列表,collectList但也阻止了为数不多的可用服务器线程之一。这可能会导致性能非常差,因为您的服务器可能会在等待该数据时被阻止,并且无法同时为其他请求提供服务。

在这种特殊情况下,情况更糟,因为应用程序完全中断。 查看控制台,我们可以看到以下内容:

WARN 3075 --- [ctor-http-nio-7] io.netty.util.concurrent.DefaultPromise  : An exception was thrown by reactor.ipc.netty.channel.PooledClientContextHandler$$Lambda$532/356589024.operationComplete()
reactor.core.Exceptions$BubblingException: java.lang.IllegalArgumentException: Channel [id: 0xab15f050, L:/127.0.0.1:59350 - R:localhost/127.0.0.1:8081] was not acquired from this ChannelPool
at reactor.core.Exceptions.bubble(Exceptions.java:154) ~[reactor-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]

这可能与应在 0.7.4.RELEASE 中修复的 reactor-netty 客户端连接池问题有关。我不知道这方面的具体情况,但我怀疑整个连接池都已损坏,因为 HTTP 响应未从客户端连接正确读取。

添加spring-boot-starter-web确实会使您的应用程序使用 Tomcat,但它主要将 Spring WebFlux 应用程序转换为 Spring MVC 应用程序(它现在支持一些反应式返回类型,但具有不同的运行时模型)。如果你想用Tomcat测试你的应用程序,你可以spring-boot-starter-tomcat添加到你的POM中,这将使用Tomcat和Spring WebFlux。

相关内容

最新更新