13

I have a springboot project which uses Springboot Resttemplate. We have moved to springboot 2.0.1 from 1.5.3 and we are trying to make the rest calls from it asynchronous by using WebClient. We used to process the string received using Resttemplate as given below. But WebClient returns only data in Mono or Flux. How can I get the data as String. Already tried block() method , but it does asynchronous calls.

@Retryable(maxAttempts = 4, value = java.net.ConnectException.class,
           backoff = @Backoff(delay = 3000, multiplier = 2))
public Mono<String> getResponse(String url) {
    return webClient.get().uri(urlForCurrent).accept(APPLICATION_JSON)
                    .retrieve()
                    .bodyToMono(String.class);
}

Present Data flow with RestTemplate

  1. Controller receives the client call
  2. provider gets the data in String format
  3. Provider processes the String
  4. Data is given to controller

Controller.java

@RequestMapping(value = traffic/, method = RequestMethod.GET,
                produces = MediaType.APPLICATION_JSON_VALUE)
public String getTraffic(@RequestParam("place") String location) throws InterruptedException, ExecutionException {
    String trafficJSON = Provider.getTrafficJSON(location)
    return trafficJSON;
}

Provider.java

public String getTrafficJSON(String location) {
    String url = ----;

    ResponseEntity<String> response = dataFetcher.getResponse(url);

    /// RESPONSEBODY IS READ AS STRING AND IT NEEDS TO BE PROCESSED
    if (null != response {
        return parser.transformJSON(response.getBody(), params);
    }

    return null;
}

DataFetcher.java

@Retryable(maxAttempts = 4,
           value = java.net.ConnectException.class,
           backoff = @Backoff(delay = 3000, multiplier = 2))
public ResponseEntity<String> getResponse(String url) {
    /* ----------------------- */
    return getRestTemplate().getForEntity(urlForCurrent, String.class);
}
Abhi
  • 319
  • 1
  • 5
  • 19

3 Answers3

41

Due to the fact that there are lot of misconception, so here I'm going to clear up some things.

Spring has officially stated that RestTemplate is in maintenence mode so if you can, use WebClient if you want to be as future proof as possible.

as stated in the RestTemplate API

NOTE: As of 5.0 this class is in maintenance mode, with only minor requests for changes and bugs to be accepted going forward. Please, consider using the org.springframework.web.reactive.client.WebClient which has a more modern API and supports sync, async, and streaming scenarios.

Non reactive application

If your application is a non-reactive application (not returning fluxes or monos to the calling clients) what you have to do is to use block() if you need the value. You can of course use Mono or Flux internally in your application but in the end you must call block() to get the concrete value that you need to return to the calling client.

Non reactive applications use for instance tomcat, undertow as the underlying server implementation, which follows the servlet specification so it will assign 1 thread per request so you will not gain the performance gains you get with a reactive application.

Reactive application

If you on the other hand you have a reactive application you should never under any circumstances ever call block() in your application. Blocking is exactly what it says, it will block a thread and block that threads execution until it can move on, this is bad in a reactive world.

You should also not call subscribe in your application unless your application is the final consumer of the response. For instance, if you are calling an api to get data and write into a database that your application is connected to. Your backend application is the final consumer. If an external client is calling your backend (for instance an react, angular app, mobile client, etc. etc.) the external client is the final consumer, and is the one subscribing. Not you.

Underlying default server implementation here is a netty server which is a non servlet, event based server that will not assign one thread to each request, the server itself is thread agnostic and any thread available will handle anything at any time during any request.

The webflux documentation clearly states that both servlet 3.1+ supported servers tomcat and jetty can be used with webflux as well as non-serlet servers netty and undertow.

How do i know what application i have?

Spring states that if you have both spring-web and spring-webflux on the classpath, the application will favor spring-web and per default start up a non-reactive application with an underlying tomcat server.

This behaviour can manually be overridden if needed as spring states.

Adding both spring-boot-starter-web and spring-boot-starter-webflux modules in your application results in Spring Boot auto-configuring Spring MVC, not WebFlux. This behavior has been chosen because many Spring developers add spring-boot-starter-webflux to their Spring MVC application to use the reactive WebClient. You can still enforce your choice by setting the chosen application type to SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE).

The “Spring WebFlux Framework”

So how to implement WebClient in accordance to code provided by the question?

@Retryable(maxAttempts = 4,
       value = java.net.ConnectException.class,
       backoff = @Backoff(delay = 3000, multiplier = 2))
public Mono<String> getResponse(String url) {
    return webClient.get()
            .uri(url)
            .exchange()
            .flatMap(response -> response.toEntity(String.class));
}

I would say this is the easiest and the most less intrusive implementation. You of course need to build a proper webclient in maybe a @Bean and autowire it into its class.

Toerktumlare
  • 12,548
  • 3
  • 35
  • 54
  • 1
    If you want to get async calls without doing reactive programming, you can use the `toFuture()` method on `Mono` and `Flux` objects. – Soupy Dec 14 '20 at 16:14
  • Agree with @Scoupy: the question asks for non-blocking asynchronous calls, whereas in the example `block()` is called, which, per definition, blocks the reactive flow until the response is received. The example is good only if what you want to do is replace `RestTemplate` with `WebClient`. – WinterBoot Mar 24 '22 at 20:05
1

The first step is to build WebClient object with the baseUrl;

WebClient webClient = WebClient.builder()
    .baseUrl("http://localhost:8080/api") //baseUrl
    .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
    .build();

Then choose the method and append the path along with the request variables or body payload.

ResponseSpec responseSpec = webClient
    .get()
    .uri(uriBuilder -> uriBuilder.path("/findById") //additional path
        .queryParam("id", id).build())
    .retrieve()
    .onStatus(HttpStatus::is4xxClientError, response -> Mono.error(new CustomRuntimeException("Error")));

Wait for the response with block() function of bodyToMono. If you want response as String, you can convert it using google's gson library.

Object response = responseSpec.bodyToMono(Object.class).block();
Gson gson = new Gson();
String str = gson.toJson(response);

If you don't want to need to know the status of the api call, you can do like following.

webClient
    .post()
    .uri(uri -> uri.path("/save").build())
    .body( BodyInserters.fromObject(payload) )
    .exchange().subscribe();
Alexpandiyan Chokkan
  • 1,025
  • 1
  • 10
  • 30
  • block() operation, stops and waits. Won't it defeat the purpose of non-blocking code? – Abhi Aug 05 '19 at 10:12
  • I have updated my answer (`post` call) which is fire & forget model. – Alexpandiyan Chokkan Aug 05 '19 at 10:21
  • I want to get the response body as String – Abhi Aug 05 '19 at 10:26
  • Again I have updated the answer. You can convert the response using google Gson library. Also If you want to read the response, you have to wait for the response. – Alexpandiyan Chokkan Aug 05 '19 at 10:41
  • Thanks, Just for a clarification .To read the response and perform parsing on it later, like given above in my present project. Is using block() the only way? – Abhi Aug 05 '19 at 10:53
  • There may or mayn't be an option. As for as I know that's the only way. Thanks. – Alexpandiyan Chokkan Aug 05 '19 at 11:16
  • 1
    There is never a valid reason to use `.block()` with WebClient. If you are needing to use it there is a bigger architectural issue with your application. – Captain Aug 05 '19 at 13:58
  • 3
    _'There is never a valid reason to use .block() with WebClient.'_ That's a harsh statement. If you'd like to adopt reactive programming slowly, step-by-step it could make sense to start with WebClient and blocking it. It does no harm in a synchronous, blocking system. Also, check out RestTemplate API docs: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html RestTemplate is deprecated and WebClient is the replacement (even in blocking systems). – Martin Tarjányi Aug 05 '19 at 19:13
  • 2
    @PaulWillems in a non reactive application there is full reason to use block due to the fact that a non reactive application runs with tomcat as the underlying server implementation that will assign one thread to each request and you dont get the same performance benefits as using jetty. – Toerktumlare Aug 05 '19 at 20:27
  • 1
    you have no reason to use Gson, weblicnt uses per default Jackson as the underlying serializer/deserializer so you could `responseSpec.bodyToMono(String.class).block()` just more overhead to indroduce an additional serializer/deserializer. And it is also bad practice to call `new` Gson on each call. – Toerktumlare Aug 05 '19 at 20:29
1

The first thing to understand is if you are needing to call .block() you might as well stick with RestTemplate, using WebClient will gain you nothing.

You need to start thinking in reactive terms if you want to gain from using WebClient. A reactive process is really just a sequence of steps, the input of each step being the output of the step before it. When a request comes in, your code creates the sequence of steps and returns immediately releasing the http thread. The framework then uses a pool of worker threads to execute each step when the input from the previous step becomes available.

The benefit is a huge gain in capacity to accept competing requests at the small cost of having to rethink the way you write code. Your application will only need a very small pool of http threads and another very small pool of worker threads.

When your controller method is returning a Mono or Flux, you have got it right and there will be no need to call block().

Something like this in it's simplest form:

@GetMapping(value = "endpoint", produces = MediaType.TEXT_PLAIN_VALUE)
@ResponseBody
@ResponseStatus(OK)
public Mono<String> controllerMethod() {

    final UriComponentsBuilder builder =
            UriComponentsBuilder.fromHttpUrl("http://base.url/" + "endpoint")
                    .queryParam("param1", "value");

    return webClient
            .get()
            .uri(builder.build().encode().toUri())
            .accept(APPLICATION_JSON_UTF8)
            .retrieve()
            .bodyToMono(String.class)
            .retry(4)
            .doOnError(e -> LOG.error("Boom!", e))
            .map(s -> {

                // This is your transformation step. 
                // Map is synchronous so will run in the thread that processed the response. 
                // Alternatively use flatMap (asynchronous) if the step will be long running. 
                // For example, if it needs to make a call out to the database to do the transformation.

                return s.toLowerCase();
            });
}

Moving to thinking in reactive is a pretty big paradigm shift, but well worth the effort. Hang in there, it's really not that difficult once you can wrap your head around having no blocking code at all in your entire application. Build the steps and return them. Then let the framework manage the executions of the steps.

Happy to provide more guidance if any of this is not clear.

Remember to have fun :)

Captain
  • 714
  • 4
  • 18
  • 1
    Thanks . But In all the examples that I went through, response is returned from controller itself and there is no need for further processing of data before passing it to the controller. But in my case I need to transform the obtained response from the third party before passing it to controller. i am confused on how can I achieve it without blocking code – Abhi Aug 05 '19 at 13:33
  • @Abhi That is precisely what I was trying to demonstrate with `.map(transformer::transform)`. This is where you would do the transformation. I will update the example a little to make it clearer. – Captain Aug 05 '19 at 13:41
  • 5
    This is flat out wrong. What you gain is a future proof client. Spring has officially said that they will deprecate `RestTemplate` in the future due to its blocking nature. So there is a lot to benefit from using `WebClient` over `RestTemplate` – Toerktumlare Aug 05 '19 at 20:31
  • 1
    the question has code that returns a concrete value from its RequestMapping so it is safe to say it is a non-reactive application we are dealing with and your answer only applies to reactive applications. – Toerktumlare Aug 05 '19 at 20:58
  • 3
    Downvoted because `The first thing to understand is if you are needing to call .block() you might as well stick with RestTemplate, using WebClient will gain you nothing.` RestTemplate is deprecated so using WebClient with blocking calls will at least gain you access to a non-deprecated API. – axiopisty Apr 27 '21 at 19:35