Spring WebFlux: Only one connection receive subscriber allowed

拟墨画扇 提交于 2019-12-01 20:24:21

WebClient.exchange() result stream is unicast

The problem here is in fact that WebClient allows only one subscriber per connection. If you try to subscribe to the same exchanged connection twice - you will get java.lang.IllegalStateException: Only one connection receive subscriber allowed.

Despite the fact that I don't see where you have tried to reuse the same connection twice, I believe that you may solve that problem by using next combination of operators:

class GeoService() {
   val client = WebClient.create("https://maps.googleapis.com/maps/api/geocode/")

   fun resolveGeoFromCity(city: String): Mono<Geo> {
       return client.get()
            .uri("json?address=$city&key=$API_KEY&language=en")
            .exchange()
            .flatMap { it.bodyToMono(String::class.java) }
            .map { parse(it) }
            .share();
   }
   ...
}

in that example, flow is configured to multicasts (shares) the original source as long as there is at least one Subscriber will be subscribed. In case if you need that all subscribers receive the same date you may replace .share with .cache operator.

Also, there is an alternative to above technique. You may replace mentioned operator with a processor and get the same sharing possibility:

class GeoService() {

   val client = WebClient.create("https://maps.googleapis.com/maps/api/geocode/")

   fun resolveGeoFromCity(city: String): Mono<Geo> {
       return client.get()
            .uri("json?address=$city&key=$API_KEY&language=en")
            .exchange()
            .flatMap { it.bodyToMono(String::class.java) }
            .map { parse(it) }
            .subscribeWith(DirectProcessor.create());
   }
   ...
}

In that case, you subscribing and running consumption of source's data exactly right after calling subscribeWith, so, potentially, in that case, you may lose some part of data, etc.

Why with Mono.just(..) everything works fine?

First of all .just is a cold operator, it allows as many as possible subscribers which receive the same data at any point in time. That is why when you tried to consume the same chunk of data from the connection twice, you did not get any exceptions.

I had a similar issue. The fix was to specify this dependency:

org.springframework:spring-webflux:5.1.4.RELEASE

As I used spring-boot it deploys the previous version of it. Unfortunately, the link to this issue is not existing now.

So now my gradle looks like this:

compile('org.springframework.boot:spring-boot-starter-data-mongodb-reactive')
compile('org.springframework.boot:spring-boot-starter-webflux')
// Next 2 dependencies are temporally here until the one above does not resolves next to at least 5.1.4 - where 
// webflux issue is resolved:
// https://github.com/rstoyanchev/spr-issue-migration-test-2/issues/17323
compile('org.springframework:spring-webflux:5.1.4.RELEASE')
compile('org.springframework:spring-web:5.1.4.RELEASE')
Juan Medina

I've done a very similar example here:

This router will get a geolocation from name and the with another service extract the sunrise and sunset time:

I've use the and method from Mono.

internal fun buildResponse(address: Mono<String>) =
        address.transform(geoLocationService::fromAddress).and(this::sunriseSunset, ::LocationResponse)

internal fun sunriseSunset(geographicCoordinates: GeographicCoordinates) =
        geographicCoordinates.toMono().transform(sunriseSunsetService::fromGeographicCoordinates)

More details of this example

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!