I don\'t understand how to use AsyncRestTemplate
effectively for making external service calls. For the code below:
class Foo {
public void doS
I don't think any of the previous answers actually achieve parallelism. The problem with @diginoise response is that it doesn't actually achieve parallelism. As soon as we call get
, we're blocked. Consider that the calls are really slow such that future1
takes 3 seconds to complete, future2
2 seconds and future3
3 seconds again. With 3 get
calls one after another, we end up waiting 3 + 2 + 3 = 8 seconds.
@Vikrant Kashyap answer blocks as well on while (!(future1 .isDone() && future2.isDone() && future3.isDone()))
. Besides the while
loop is a pretty ugly looking piece of code for 3 futures, what if you have more? @lkz answer uses a different technology than you asked for, and even then, I'm not sure if zip
is going to do the job. From Observable Javadoc:
zip applies this function in strict sequence, so the first item emitted by the new Observable will be the result of the function applied to the first item emitted by each of the source Observables; the second item emitted by the new Observable will be the result of the function applied to the second item emitted by each of those Observables; and so forth.
Due to Spring's widespread popularity, they try very hard to maintain backward compatibility and in doing so, sometimes make compromises with the API. AsyncRestTemplate
methods returning ListenableFuture
is one such case. If they committed to Java 8+, CompletableFuture
could be used instead. Why? Since we won't be dealing with thread pools directly, we don't have a good way to know when all the ListenableFutures have completed. CompletableFuture
has an allOf
method that creates a new CompletableFuture
that is completed when all of the given CompletableFutures complete. Since we don't have that in ListenableFuture
, we will have to improvise.
I've not compiled the following code but it should be clear what I'm trying to do. I'm using Java 8 because it's end of 2016.
// Lombok FTW
@RequiredArgsConstructor
public final class CounterCallback implements ListenableFutureCallback> {
private final LongAdder adder;
public void onFailure(Throwable ex) {
adder.increment();
}
public void onSuccess(ResponseEntity result) {
adder.increment();
}
}
ListenableFuture> f1 = asyncRestTemplate
.getForEntity(url1, String.class);
f1.addCallback(//);
// more futures
LongAdder adder = new LongAdder();
ListenableFutureCallback> callback = new CounterCallback(adder);
Stream.of(f1, f2, f3)
.forEach {f -> f.addCallback(callback)}
for (int counter = 1; adder.sum() < 3 && counter < 10; counter++) {
Thread.sleep(1000);
}
// either all futures are done or we're done waiting
Map> futures = Stream.of(f1, f2, f3)
.collect(Collectors.partitioningBy(Future::isDone));
Now we've a Map
for which futures.get(Boolean.TRUE)
will give us all the futures that have completed and futures.get(Boolean.FALSE)
will give us the ones that didn't. We will want to cancel the ones that didn't complete.
This code does a few things that are important with parallel programming: