How to use AsyncRestTemplate to make multiple calls simultaneously?

前端 未结 6 2055
耶瑟儿~
耶瑟儿~ 2021-02-18 22:42

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

class Foo {

    public void doS         


        
6条回答
  •  星月不相逢
    2021-02-18 23:20

    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:

    1. It doesn't block.
    2. It limits the operation to some maximum allowed time.
    3. It clearly separates successful and failure cases.

提交回复
热议问题