block()/blockFirst()/blockLast() are blocking error when calling bodyToMono AFTER exchange()

We Are Going To Discuss About block()/blockFirst()/blockLast() are blocking error when calling bodyToMono AFTER exchange(). So lets Start this Java Article.

block()/blockFirst()/blockLast() are blocking error when calling bodyToMono AFTER exchange()

  1. block()/blockFirst()/blockLast() are blocking error when calling bodyToMono AFTER exchange()

    You should never call a blocking method within a method that returns a reactive type; you will block one of the few threads of your application and it is very bad for the application

  2. block()/blockFirst()/blockLast() are blocking error when calling bodyToMono AFTER exchange()

    You should never call a blocking method within a method that returns a reactive type; you will block one of the few threads of your application and it is very bad for the application

Solution 1

First, a few things that will help you understand the code snippet solving this use case.

  1. You should never call a blocking method within a method that returns a reactive type; you will block one of the few threads of your application and it is very bad for the application
  2. Anyway as of Reactor 3.2, blocking within a reactive pipeline throws an error
  3. Calling subscribe, as suggested in the comments, is not a good idea either. It is more or less like starting that job as a task in a separate thread. You’ll get a callback when it’s done (the subscribe methods can be given lambdas), but you’re in fact decoupling your current pipeline with that task. In this case, the client HTTP response could be closed and resources cleaned before you get a chance to read the full response body to write it to a file
  4. If you don’t want to buffer the whole response in memory, Spring provides DataBuffer (think ByteBuffer instances that can be pooled).
  5. You can call block if the method you’re implementing is itself blocking (returning void for example), for example in a test case.

Here’s a code snippet that you could use to do this:

Mono<Void> fileWritten = WebClient.create().post()
        .uri(uriBuilder -> uriBuilder.path("/file/").build())
        .exchange()
        .flatMap(response -> {
            if (MediaType.APPLICATION_JSON_UTF8.equals(response.headers().contentType().get())) {
                Mono<NoPayloadResponseDto> dto = response.bodyToMono(NoPayloadResponseDto.class);
                return createErrorFile(dto);
            }
            else {
                Flux<DataBuffer> body = response.bodyToFlux(DataBuffer.class);
                return createSpreadsheet(body);
            }
        });
// Once you get that Mono, you should give plug it into an existing
// reactive pipeline, or call block on it, depending on the situation

As you can see, we’re not blocking anywhere and methods dealing with I/O are returning Mono<Void>, which is the reactive equivalent of a done(error) callback that signals when things are done and if an error happened.

Since I’m not sure what the createErrorFile method should do, I’ve provided a sample for createSpreadsheet that just writes the body bytes to a file. Note that since databuffers might be recycled/pooled, we need to release them once we’re done.

private Mono<Void> createSpreadsheet(Flux<DataBuffer> body) {
    try {
        Path file = //...
        WritableByteChannel channel = Files.newByteChannel(file, StandardOpenOption.WRITE);
        return DataBufferUtils.write(body, channel).map(DataBufferUtils::release).then();
    } catch (IOException exc) {
        return Mono.error(exc);
    }
}

With this implementation, your application will hold a few DataBuffer instances in memory at a given time (the reactive operators are prefetching values for performance reasons) and will write bytes as they come in a reactive fashion.

Original Author Brian Clozel Of This Content

Solution 2

[UPDATE 2021/10/19]

toProcessor() is now deprecated.

Consider using

myMono.toFuture().get();

As stated in the most voted answer, one should never block. In my case, that is the only option as we are using a reactive library within an imperative piece of code. The blocking can be done by wrapping the mono in a processor:

myMono.toProcessor().block()

Original Author adelinor Of This Content

Solution 3

To execute Client Request outside the Server Request pool, use myWebClientMono.share().block();

Original Author Anton Seredkin Of This Content

Solution 4

For me adding web dependency solved the problem.

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

Original Author Pradyskumar Of This Content

Conclusion

So This is all About This Tutorial. Hope This Tutorial Helped You. Thank You.

Also Read,

Siddharth

I am an Information Technology Engineer. I have Completed my MCA And I have 4 Year Plus Experience, I am a web developer with knowledge of multiple back-end platforms Like PHP, Node.js, Python and frontend JavaScript frameworks Like Angular, React, and Vue.

Leave a Comment