spring-projects / spring-framework

Spring Framework
https://spring.io/projects/spring-framework
Apache License 2.0
55.28k stars 37.62k forks source link

"multiple subscribers not supported" when using WebClient exchange #32727

Closed poutsma closed 2 weeks ago

poutsma commented 2 weeks ago

With Jetty Reactive Client 4.0.2+, subscribing to a publisher multiple times results in test timeouts. To get rid of those, we introduced guards against multiple subscriptions in 6.1.4, see #32100, after a suggestion from the Jetty team (see https://github.com/jetty-project/jetty-reactive-httpclient/issues/354#issuecomment-1892851332).

This guard was backported to 6.0.17 (see #32102), and 5.0.3.32 (see #32101). However, it seems that this change is causing issues on 6.0.x (and potentially also 5.3.x) when used in combination with WebClient's exchange method, for instance:

        Mono<String> responseEntityMono = webClient
                .post().uri("http://localhost:8081")
                .exchangeToMono( response -> response.bodyToMono(String.class))

Specifically, this code results in an IllegalStateException thrown here: https://github.com/jetty-project/jetty-reactive-httpclient/blob/3.0.x/src/main/java/org/eclipse/jetty/reactive/client/internal/AbstractSinglePublisher.java#L49. The error is only logged, and can safely be ignored. However, it does potentially spam the log, so should be fixed.

~The cause of this problem is unclear. However, removing the aforementioned guard resolved it.~ See https://github.com/spring-projects/spring-framework/issues/32727#issuecomment-2085128845

Since the original issue (#32100) only occurred with Jetty Reactive Client 4.0.2+, it appears that we can remove the guard in 6.0.x (using Jetty Reactive Client 3.0.x) and 5.3.x (JRC 1.1.x), and only keep them in 6.1.x and higher.

poutsma commented 2 weeks ago

~After doing more investigation, it seems that the issue does not occur with Spring Framework 5.3.x, only 6.0.17+. We therefore do not need to back port.~ See below.

poutsma commented 2 weeks ago

The reason for the IllegalStateException from Jetty appears to be that Flux::onSubscribe cannot be used to block a subscription, and that was the mechanism used by the AbstractClientHttpResponse. Why the behavior was only triggered by Jetty Reactive Client 3 is unclear.

The original guard against multiple subscriptions is issue #32100, which was fixed in 6.1.x and backported to 6.0.x and 5.3.x. The fix for this issue should be backported to the same branches, despite my earlier comments.