簡體   English   中英

如何使用AsyncRestTemplate同時進行多個調用?

[英]How to use AsyncRestTemplate to make multiple calls simultaneously?

我不明白如何有效地使用AsyncRestTemplate進行外部服務調用。 對於以下代碼:

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();
    }
}

理想情況下,我希望同時執行所有3個調用,並在完成所有操作后處理結果。 但是,在調用get()之前不會獲取每個外部服務調用,但get()阻止get() 那么這不是打敗了AsyncRestTemplate的目的嗎? 我不妨使用RestTemplate

所以我不明白我怎么能讓它們同時執行?

在調度所有異步調用之前,不要調用阻塞get()

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();
  }
}

你既可以做調度 ,並在循環中得到的,但要注意,目前的結果是采集效率低下,因為它會卡住的下一個未完成的未來。

您可以將所有期貨添加到集合中,並迭代測試每個未來非阻塞isDone() 當該調用返回true時,您可以調用get()

這樣,您的整體結果收集將被優化,而不是按照調用get() s的順序等待下一個緩慢的未來結果。

更妙的是,你可以注冊每個內回調(運行時) ListenableFuture由歸國AccyncRestTemplate ,你不必擔心周期性檢查可能的結果。

如果您不必使用'AsyncRestTemplate',我建議使用RxJava。 RxJava zip運算符正是您所需要的。 檢查以下代碼:

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));
}

所有對外部服務的請求都將在不同的線程中執行,當最后一次調用完成時,將應用轉換函數(在示例中簡單的字符串連接),結果(連接字符串)將從'zip'可觀察到。

我理解你的問題是你有一個預定義的異步方法,你嘗試做的是使用RestTemplate類異步調用這個方法。

我寫了一個方法,可以幫助你異步調用你的方法。

 public void testMyAsynchronousMethod(String... args) throws Exception {
        // Start the clock
        long start = System.currentTimeMillis();

        // Kick of multiple, asynchronous lookups
        Future<String> future1 = asyncRestTemplate
        .getForEntity(url1, String.class);;
        Future<String> future2 = asyncRestTemplate
        .getForEntity(url2, String.class);
        Future<String> future3 = asyncRestTemplate
        .getForEntity(url3, String.class);

        // Wait until they are all done
        while (!(future1 .isDone() && future2.isDone() && future3.isDone())) {
            Thread.sleep(10); //10-millisecond pause between each check
        }

        // Print results, including elapsed time
        System.out.println("Elapsed time: " + (System.currentTimeMillis() - start));
        System.out.println(future1.get());
        System.out.println(future2.get());
        System.out.println(future3.get());
    }

您可能想要使用CompletableFuture類( javadoc )。

  1. 將您的調用轉換為CompletableFuture 例如。

     final CompletableFuture<ResponseEntity<String>> cf = CompletableFuture.supplyAsync(() -> { try { return future.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } }); 
  2. 接下來使用您新創建的3個可完成期貨調用CompletableFuture::allOf方法。

  3. 在結果上調用join()方法。 在得到的可完成的未來得到解決后,您可以從步驟3中創建的每個單獨的可完成的未來中獲得結果。

我想你在這里誤解了一些事情。 當您調用getForEntity方法時,請求已被觸發。 當調用future對象的get()方法時,您只是在等待請求完成。 因此,為了在同一亞秒內觸發所有這三個請求,您只需要:

// Each of the lines below will fire an http request when it's executed
Future<ResponseEntity<String>> future1 = asyncRestTemplate.getForEntity(url1, String.class);
Future<ResponseEntity<String>> future2 = asyncRestTemplate.getForEntity(url2, String.class);
Future<ResponseEntity<String>> future3 = asyncRestTemplate.getForEntity(url3, String.class);

運行所有這些代碼后,所有請求都已被觸發(最可能在同一亞秒內)。 然后你可以隨心所欲地做任何你想做的事。 只要調用任何get()方法,就會等待每個請求完成。 如果它們已經完成,那么它將立即返回。

// do whatever you want in the meantime
// get the response of the http call and wait if it's not completed
String response1 = future1.get();
String response2 = future2.get();
String response3 = future3.get();

我認為之前的任何答案都沒有實際實現並行性。 @diginoise響應的問題在於它實際上並沒有實現並行性。 一旦我們打電話給get ,我們就被阻止了。 考慮到調用非常慢,因此future1需要3秒才能完成, future2 2秒和future3 3秒再次完成。 隨着3 get的呼聲此起彼伏,我們最終等待3 + 2 + 3 = 8秒。 @Vikrant Kashyap同時回答塊while (!(future1 .isDone() && future2.isDone() && future3.isDone())) 除了while循環是一個非常難看的3個期貨的代碼片段,如果你有更多呢? @lkz答案使用的技術與你要求的不同,即便如此,我也不確定zip是否可以完成這項工作。 來自Observable Javadoc:

zip以嚴格的順序應用此函數,因此新Observable發出的第一個項將是應用於每個源Observable發出的第一個項的函數的結果; 新Observable發出的第二個項目將是應用於每個Observable發出的第二個項目的函數的結果; 等等。

由於Spring廣泛流行,他們非常努力地保持向后兼容性,並且這樣做有時會對API做出妥協。 返回ListenableFuture AsyncRestTemplate方法就是這種情況。 如果他們致力於Java 8+,則可以使用CompletableFuture 為什么? 由於我們不會直接處理線程池,因此我們無法知道所有ListenableFutures何時完成。 CompletableFuture有一個allOf方法,可以創建一個新的CompletableFuture ,在所有給定的CompletableFutures完成時完成。 由於我們在ListenableFutureListenableFuture ,我們將不得不即興發揮。 我沒有編譯下面的代碼,但應該清楚我正在嘗試做什么。 我正在使用Java 8,因為它是2016年底。

// Lombok FTW
@RequiredArgsConstructor
public final class CounterCallback implements ListenableFutureCallback<ResponseEntity<String>> {
  private final LongAdder adder;

  public void onFailure(Throwable ex) {
    adder.increment();
  }
  public void onSuccess(ResponseEntity<String> result) {
    adder.increment();
  }
}

ListenableFuture<ResponseEntity<String>> f1 = asyncRestTemplate
        .getForEntity(url1, String.class);
f1.addCallback(//);
// more futures

LongAdder adder = new LongAdder();
ListenableFutureCallback<ResponseEntity<String>> 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<Boolean, ResponseEntity<String>> futures = Stream.of(f1, f2, f3)
  .collect(Collectors.partitioningBy(Future::isDone));

現在我們有一個Map ,其中futures.get(Boolean.TRUE)將為我們提供已經完成的所有期貨, futures.get(Boolean.FALSE)將為我們提供那些沒有完成的futures.get(Boolean.FALSE) 我們希望取消那些沒有完成的。

這段代碼做了一些對並行編程很重要的事情:

  1. 它不會阻止。
  2. 它將操作限制在某個最大允許時間。
  3. 它明確區分了成功案例和失敗案例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM