web-dev-qa-db-ja.com

AsyncRestTemplateを使用して複数の呼び出しを同時に行う方法

外部サービス呼び出しを行うために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つの呼び出しすべてを同時に実行し、すべて完了したら結果を処理したいです。 ただし、各外部サービス呼び出しはnotget()が呼び出されるまでフェッチされるただし、get()はブロックされます。だから、それはAsyncRestTemplateの目的に反しませんか? RestTemplateも使用できます。

だから私はそれらを同時に実行させる方法を理解していないのですか?

16
Glide

すべての非同期呼び出しをディスパッチする前に、ブロック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();
  }
}
_

dispatchgetの両方をループで実行できますが、現在の結果の収集は、次の未完成の未来で行き詰まるため、効率が悪いことに注意してください。

すべてのフューチャをコレクションに追加し、それを反復処理して、各フューチャの非ブロッキングisDone()をテストできます。その呼び出しがtrueを返したら、get()を呼び出すことができます。

これにより、get() sを呼び出す順序で次の遅い将来の結果を待つのではなく、大量の結果収集が最適化されます。

さらに良いのは、ListenableFutureによって返される各AccyncRestTemplate内にコールバック(ランタイム)を登録でき、潜在的な結果を周期的に検査することを心配する必要がないことです。

15
diginoise

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

外部サービスへのすべてのリクエストは個別のスレッドで実行され、最後の呼び出しが終了すると、変換関数(例:単純な文字列の連結)が適用され、結果(連結された文字列)がobservableから送信されます。

6
lkz

あなたの質問で理解しているのは、事前定義された非同期メソッドがあり、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());
    }
6
Vikrant Kashyap

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で作成した各完成した未来から結果を取得できます。

1
Kuvaldis