How to properly drain / release response body from WebFlux Client?

Sokolov :

I am using WebFlux HTTP Client from Spring 5 reactive stack to access external REST service. I want to process responses based on HTTP status:

  1. If status is 2xx I want to return Mono with deserizlized response body.

  2. If status is 404 I want to drop response body and immediately return empty Mono.

  3. For any other status I want to drop response body and return error Mono with MyBusinessException.

My code looks like this:

webClient.get()
    .uri("/search")
    .syncBody(request)
    .exchange()
    .flatMap { response ->
        when {
            response.statusCode().is2xxSuccessful -> response.bodyToMono(MyResponse::class.java)
            response.statusCode() == NOT_FOUND -> Mono.empty()
            else -> MyBusinessException().toMono<MyResponse>()
        }
     }

I don't want to waste time to receive and process response body when I don't need it. JavaDoc for exchange() method states

You must always use one of the body or entity methods of the response to ensure resources are released.

How exactly should I do this if I want to drain response body and return result immediately?

Phil Clay :

Draining the response is needed so that the connection can be reused for future requests (i.e. http keep-alive / persistent connections).

To return an empty Mono that completes after the body is drained (ignoring errors):

// drain the body
response.body(BodyExtractors.toDataBuffers())
    // release DataBuffers
    .doOnNext(DataBufferUtils::release)
    // ignore errors
    .onErrorResume(exception -> Mono.empty())
    // return an empty Mono
    .then();

To return an empty Mono that completes immediately, and drain the body (ignoring errors) asynchronously in the background:

Mono.<Void>empty()
    .doOnSubscribe(s ->
        // drain the body
        response.body(BodyExtractors.toDataBuffers())
            // release DataBuffers
            .doOnNext(DataBufferUtils::release)
            // initiate drain on a separate Scheduler
            .subscribeOn(Schedulers.parallel())
            // subscribe, and ignore errors
            .subscribe())

I'd still recommend the first option, since it frees up resources immediately, and is probably what the WebClient developers had in mind when writing it and documenting its usage.

I've never used the second option in a production system, so do your own testing to ensure the http connection pooling is behaving as desired. If using reactor-netty, you can enable debug logging on reactor.netty.resources.PooledConnectionProvider to compare the two approaches.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=79438&siteId=1