如何使用AsyncRestTemplate同时进行多个调用?

时间:2016-02-05 00:38:32

标签: java spring resttemplate

我不明白如何有效地使用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()被阻止之前,每次外部服务调用 都会被提取。那么是不是会破坏AsyncRestTemplate的目的?我不妨使用RestTemplate

所以我不知道如何让他们同时执行?

6 个答案:

答案 0 :(得分:14)

在发送所有异步调用之前,不要调用阻塞<div> style=\"color:black;

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(); } } 。当该调用返回true时,您可以调用isDone()

这样,您的整体结果收集将得到优化,而不是按照调用get() s的顺序等待下一个缓慢的未来结果。

更好的是,您可以在get()返回的每个ListenableFuture内注册回调(运行时),并且您不必担心会周期性地检查潜在结果。

答案 1 :(得分:6)

如果您不必使用&#39; AsyncRestTemplate&#39;我建议改用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));
}

所有对外部服务的请求都将在不同的线程中执行,当最后一次调用完成时,将应用转换函数(例如简单字符串连接),结果(连接字符串)将从&#39; zip&#39开始;观察到。

答案 2 :(得分:6)

我对您的问题的理解是您有一个预定义的异步方法,而您尝试使用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());
    }

答案 3 :(得分:1)

您可能希望使用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中创建的每个单独的可完成的未来中获得结果。

答案 4 :(得分:-1)

我认为你在这里误解了一些事情。当您调用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();

答案 5 :(得分:-2)

我认为之前的任何答案都没有实现并行性。 @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发出的第二个项目将是结果   应用于每个项发出的第二个项的函数   观测;等等。

由于Spring的广泛普及,他们非常努力地保持向后兼容性,并且这样做有时会对API做出妥协。返回AsyncRestTemplate的{​​{1}}方法就是这种情况。如果他们致力于Java 8+,则可以使用ListenableFuture。为什么?由于我们不会直接处理线程池,因此我们没有很好的方法知道所有ListenableFutures何时完成。 CompletableFuture有一个CompletableFuture方法,可以创建一个新的allOf,在所有给定的CompletableFutures完成时完成。由于我们在CompletableFuture中没有这个,我们将不得不即兴发挥。 我没有编译下面的代码,但我应该清楚我要做的事情。我使用的是Java 8,因为它已经过了2016年。

ListenableFuture

现在我们已经// 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)将为我们提供那些没有完成的未来。我们希望取消那些尚未完成的内容。

这段代码做了一些对并行编程很重要的事情:

  1. 它没有阻止。
  2. 它将操作限制在某个最大允许时间。
  3. 明确区分成功案例和失败案例。