Spring RestTemplate vs WebClient for sync requests
Asked Answered
M

3

5

Sorry if this was asked before, but I didn't find a matching question.

I have an application that performs api calls to other services. I'm thinking of using WebClient over RestTemplate as it's advised by Spring. I'm performing exclusively Synchronous HTTP calls.

I know WebClient is designed with Reactive approach in mind, but in theory: Is it ok to use WebClient solely for blocking calls? I'm concerned by the fact that I have to call .block() on each call to get the data. So my questions are:

  1. How safe is it to use .block() and is it ok in general to block threads in WebClient?
  2. Is the mechanics behind blocking calls with WebClient similar to what RestTemplate does?
  3. Is there a possibility that the performance would be worse than in case I just use RestTemplate?

Thanks in advance!

Microclimatology answered 15/2, 2022 at 14:17 Comment(0)
A
12

Since there seems to be misunderstandings i will try to answer the questions to the best of my knowledge.

How safe is it to use .block() and is it ok in general to block threads in WebClient?

Blocking is always safe but whether or not it affects performance is another thing. When a request comes in, it gets assigned a thread. When we do a request using RestTemplate the same thread will do the external request, and RestTemplate will block that thread under the hood in wait for the response.

This is by no means efficient usage of a thread, but its completely safe, and this is how most web servers in general have been working for the past 20 years.

When using WebClient in a non-reactive application and you block a Mono<T> (which you essentially will be doing) the framework will first check that the thread is a type of thread that you are allowed to block (not a nio-thread) and after that it uses a CountDownLatch which pauses/blocks the calling thread at CountDownLatch#await until the first onNext/onComplete/onError signal arrives. This is completely fine in a blocking application. You can find the relevant code here.

When you add WebClient to the class path you will automatically get netty as the underlying server, which could be good to know. If you wish to change that out, then you need to be explicit about it.

Also, it is recommended that if you do multiple requests, then you should chain on as many reactive calls as possible before resorting to block.

If you want to move over to a reactive application, then this is a very good way of slowly transferring an application, by slowly doing more and more reactive stuff and then calling block to return to the regular world.

Are you fully reactive? no, are you a blocking web server like before, well yes. Is it worse that RestTemplate most likely not. Are you better than before, from a maintenance standpoint, yes since spring has officially gone out with that there is not going to be any more updates to RestTemplate.

Is the mechanics behind blocking calls with WebClient similar to what RestTemplate does?

Well this is hard to tell, because RestTemplate is mainly just a wrapper around the HttpClient provided by the underlying server implementation.

How the blocking is written is probably different, but what they do in the end, is most likely the same. A Mono<T> blocks using a CountDownLatch with repeated calls to getCount and then inbetween blocks with a call on latch await until the response is back. I havn't looked at the different HttpClients that RestTemplate is wrapping, you need to read up on each of them (tomcat, jetty, undertow, etc. etc.)

Is there a possibility that the performance would be worse than in case I just use RestTemplate?

This is extremely hard to tell because performance is not black and white. It all depends on hardware, what type of jobs are to be done, how the code is written, thread pool sizes, operating system etc..

Netty is a fully event driven server and it is starting to become the de facto standard of web server in the Java community. Undertow decided to switch out their entire core to the netty core, because it was so good, and easier to maintain.

As Netty is event driven, running it as an old server with one thread per request could hurt performance since it is not optimized for that type of work, but on the other hand when you run it fully event driven it will shine.

The only way to answer this, is to do your own benchmarks, we can't answer that for you.

If you want to know more about how netty actually works under the hood a recommend reading the book Netty in Action which is not free but very good read to understand the innerworkings of Netty and its async threading model.

Angus answered 16/2, 2022 at 11:15 Comment(5)
Thanks, I think this is the kind of answer I was expecting. I'd upvote it but don't have enough reputation:( Nevertheless you were very helpful and I appreciate the time you spent on it!Microclimatology
Just a small correction: the await link goes to a test util method. That's not how block is implemented. The real implementation uses a CountDownLatch.Hill
that is true, updated the linkAngus
"which pauses at CountDownLatch#await until there are no threads left" - What do you mean by "no threads left"? It waits until the first next/complete/error event.Hill
I know how CountDownLatch works. That's why I asked the question because it wasn't clear for me what threads you are referring to. I found that a bit confusing. I'll add my edit. Thank you for the opportunity. Otherwise a great answer!Hill
B
2

In our applications we are migrating from RestTemplate to WebClient without any issues, .block() works just fine

Response response = this.webClient
        .post()
        .uri(uri)
        .body(fromValue)
        .retrieve()
        .bodyToMono(Response.class)
        .timeout(Duration.ofMillis(timeoutMillis))
        .block();

This is doing the same as RestTemplate does, it's sending the request in a synchronized way, and we have it working in PROD since a couple of months without any issues

Bilicki answered 15/2, 2022 at 14:24 Comment(10)
Thanks for your reply! So I suppose it's ok in general to block threads in WebClient and nothing to be afraid of, unless it's done in a reactive application?Microclimatology
Right, I don't know what happens with a .block() in a reactive application. For reactive apps we use Mono<> responsesBilicki
Just wondering - wouldn't we exhaust the web client's thread pool if for example there will not be responses for a long period of time and we use requests without the timeout? Is it really okay to use a timeout to terminate the request threads? Maybe I'm wrong, I'm doing parallels to the CompletableFuture and another asynchronous framework such as Play! where it is really bad idea and conceptually wrong to block or time out the requests as it leads to performance degradation and makes no sense in use of reactive client at all?Oxbow
Yes for sure, having several requests at the same time without timeout would exhaust the pool, that's why we use a timeout. For our use case, this works perfectly, but for sure it wouldn't be ideal for other use cases. For that scenario, I would recommend using WebClient in an asynchronous wayBilicki
Please don't do this. This brings no value whatsoever to your application when compared to the good old RestTemplate.Tartarus
That is your opinion Which is simply not true. Springs recommendation is to move forward with webclient, as resttemplate is in maintnance mode.Angus
This brings no value, but also doesn't do anything bad? @JoãoDiasMicroclimatology
also if you wish to use timouts dont use duration, this is how you configure it docs.spring.io/spring-framework/docs/current/reference/html/…Angus
@Microclimatology it isn't bad, but I would stick with RestTemplate until it is deprecated at any point in time. Using WebClient in a non-reactive application brings little value to it (depending on how you use it, but as depicted in this answer it has no additional value) and makes it harder to read. Using block() in a reactive application is worse and that was what I thought you were doing.Tartarus
Thanks for your input! Harder to read is indeed a personal preference, but I get your general idea :)Microclimatology
C
0

It is worth adding to the previous responses that if you want to use webClient in a blocking way while using only spring-boot-starter-webflux dependency, will throw an exception like block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3, so in order to use Webclient in a blocking way you need to configure a spring MVC application by adding spring-boot-starter-web as stated in the documentation Web Environment:

A SpringApplication attempts to create the right type of ApplicationContext on your behalf. The algorithm used to determine a WebApplicationType is the following:

If Spring MVC is present, an AnnotationConfigServletWebServerApplicationContext is used

If Spring MVC is not present and Spring WebFlux is present, an AnnotationConfigReactiveWebServerApplicationContext is used

Otherwise, AnnotationConfigApplicationContext is used

This means that if you are using Spring MVC and the new WebClient from Spring WebFlux in the same application, Spring MVC will be used by default. You can override that easily by calling setWebApplicationType(WebApplicationType).

Capitalist answered 5/2, 2023 at 11:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.