Hello I try to use WebClient (netty) in synchronous style:
return internalWebClient
.post()
.uri(composeUri(ACCOUNTS_LIST_URL))
.syncBody(userCodes)
.retrieve()
.bodyToFlux(UserResponse.class)
.collectList()
.block();
Documentation - https://github.com/spring-projects/spring-framework/blob/master/src/docs/asciidoc/web/webflux-webclient.adoc - says that it's ok but I got IllegalStateException
java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-4
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:77)
at reactor.core.publisher.Mono.block(Mono.java:1494)
Can you explain how to use WebClient in synchronous style / what's wrong / is documentation correct?
Comment From: ilya-murzinov
It means that you are executing this code in a thread which can not be blocked, in your case reactor-http-nio-4
.
If you run this code in main
thread, or any other, it will work.
Comment From: fedotxxl
After a lot of debugging, I realized that I can't block in map method of webClient
webClientInternal.get()
.uri("https://ya.ru")
.retrieve()
.bodyToMono(String.class)
.map(s -> {
return webClientInternal.get()
.uri("https://ya.ru")
.retrieve()
.bodyToMono(String.class)
.block();
})
.block();
I fixed it with:
webClientInternal.get()
.uri("https://ya.ru")
.retrieve()
.bodyToMono(String.class)
.publishOn(Schedulers.elastic())
// .publishOn(Schedulers.parallel())
// .publishOn(Schedulers.single())
.map(s -> {
return webClientInternal.get()
.uri("https://ya.ru")
.retrieve()
.bodyToMono(String.class)
.block();
})
.block();
```
It's weird for me but looks like it's ok in reactor
**Comment From: rstoyanchev**
To be more precise, you can't block when making a nested call, which in this case happens to be a `map`, because at that point you're on a Reactor Netty (event loop) thread, processing a response from the server.
There is no need to block. Use `flatMap` for such nested calls:
```java
webClientInternal.get()
.uri("https://ya.ru")
.retrieve()
.bodyToMono(String.class)
.flatMap(s ->
webClientInternal.get()
.uri("https://ya.ru")
.retrieve()
.bodyToMono(String.class)
)
.block();
Comment From: sagar11988
The potential issue because of you tried to call blocking the operation with a non-Blocking Netty container. Please add below dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
if you want to use WebClient in a blocking form, it seems that you need to operate it in a threaded environment that supports blocking
Code Snippet for reference :
Comment From: canereren123
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-
Your suggestion is wrong, because you are making the project synhronus and then it will work since it is not using netty http threads anymore. When you configure the application as above then the project is not using webflux anymore instead it uses spring web shich is synhronus. If you remove spring-boot-starter-web you will have the same problem again.
spring-boot-starter-web : Synchronus Http Servlet Request spring-boot-starter-webflux : Asynchronus Http Netty Request
Comment From: rstoyanchev
The snippets shown are not related to running on WebFlux and would work that way even in a simple client app without a server main
method. It's that there are two remote calls, one nested within the other, so the nested one ends up being executed on a Reactor Netty thread.
Note however that in this case, with multiple remote calls involving a reactive client, there is no need to block within the Reactor chain, and no need to switch threads via publishOn
. You can declare the call sequence without blocking, and then block only at the end. So, remove the block()
for the nested call and flatMap
instead of map
:
webClientInternal.get()
.uri("https://ya.ru")
.retrieve()
.bodyToMono(String.class)
.flatMap(s -> {
return webClientInternal.get()
.uri("https://ya.ru")
.retrieve()
.bodyToMono(String.class);
})
.block();
Comment From: morohon
@rstoyanchev I apologize for bringing up an old issue. But I ran into a similar problem when using the interceptor. The problem was described here: https://stackoverflow.com/questions/77360338/spring-webclient-interceptor-in-synchronous-mode
Do I understand correctly that, judging by the issues, it is not possible to use the interceptor in blocking mode? Maybe there are some ways out?
Comment From: bclozel
@morohon I've replied to your SO question. The same advice applies there, the flatMap
operator helps to compose ansynchronous operations.