Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use AsyncRestTemplate to make multiple calls simultaneously?

I don't understand how to use AsyncRestTemplate effectively for making external service calls. For the code below:

class Foo {

    public void doStuff() {
        Future<ResponseEntity<String>> future1 = asyncRestTemplate.getForEntity(
                url1, String.class);
        String response1 = future1.get();

        Future<ResponseEntity<String>> future2 = asyncRestTemplate.getForEntity(
                url2, String.class);
        String response2 = future2.get();

        Future<ResponseEntity<String>> future3 = asyncRestTemplate.getForEntity(
                url3, String.class);
        String response3 = future3.get();
    }
}

Ideally I want to execute all 3 calls simultaneously and process the results once they're all done. However each external service call is not fetched until get() is called but get() is blocked. So doesn't that defeat the purpose of AsyncRestTemplate? I might as well use RestTemplate.

So I don't understaand how I can get them to execute simultaneously?

like image 812
Glide Avatar asked Feb 05 '16 00:02

Glide


People also ask

How do you call multiple services parallel in spring boot?

So you want to parallelize these two independent calls. To do so, you have to do the following steps : Add @Async annotation to the function you want to parallelize getCountriesByLanguage and getCountriesByRegion. Change the return type of the function by CompletableFuture<List<Country>>

What is AsyncRestTemplate?

AsyncRestTemplate is the main class for client side HTTP access. The access is asynchronous. A URL can be invoked asynchronously within a program which will return ListenableFuture object.

What is the difference between WebClient and RestTemplate?

RestTemplate uses Java Servlet API and is therefore synchronous and blocking. Conversely, WebClient is asynchronous and will not block the executing thread while waiting for the response to come back. The notification will be produced only when the response is ready. RestTemplate will still be used.


2 Answers

Simply don't call blocking get() before dispatching all of your asynchronous calls:

class Foo {
  public void doStuff() {
    ListenableFuture<ResponseEntity<String>> future1 = asyncRestTemplate
        .getForEntity(url1, String.class);
    ListenableFuture<ResponseEntity<String>> future2 = asyncRestTemplate
        .getForEntity(url2, String.class);
    ListenableFuture<ResponseEntity<String>> future3 = asyncRestTemplate
        .getForEntity(url3, String.class);

    String response1 = future1.get();
    String response2 = future2.get();
    String response3 = future3.get();
  }
}

You can do both dispatch and get in loops, but note that current results gathering is inefficient as it would get stuck on the next unfinished future.

You could add all the futures to a collection, and iterate through it testing each future for non blocking isDone(). When that call returns true, you can then call get().

This way your en masse results gathering will be optimised rather than waiting on the next slow future result in the order of calling get()s.

Better still you can register callbacks (runtimes) within each ListenableFuture returned by AccyncRestTemplate and you don't have to worry about cyclically inspecting the potential results.

like image 52
diginoise Avatar answered Oct 10 '22 19:10

diginoise


If you don't have to use 'AsyncRestTemplate' I would suggest to use RxJava instead. RxJava zip operator is what you are looking for. Check code below:

private rx.Observable<String> externalCall(String url, int delayMilliseconds) {
    return rx.Observable.create(
            subscriber -> {
                try {
                    Thread.sleep(delayMilliseconds); //simulate long operation
                    subscriber.onNext("response(" + url + ") ");
                    subscriber.onCompleted();
                } catch (InterruptedException e) {
                    subscriber.onError(e);
                }
            }
    );
}

public void callServices() {
    rx.Observable<String> call1 = externalCall("url1", 1000).subscribeOn(Schedulers.newThread());
    rx.Observable<String> call2 = externalCall("url2", 4000).subscribeOn(Schedulers.newThread());
    rx.Observable<String> call3 = externalCall("url3", 5000).subscribeOn(Schedulers.newThread());
    rx.Observable.zip(call1, call2, call3, (resp1, resp2, resp3) -> resp1 + resp2 + resp3)
            .subscribeOn(Schedulers.newThread())
            .subscribe(response -> System.out.println("done with: " + response));
}

All requests to external services will be executed in separate threads, when last call will be finished transformation function( in example simple string concatenation) will be applied and result (concatenated string) will be emmited from 'zip' observable.

like image 30
lkz Avatar answered Oct 10 '22 19:10

lkz