简体   繁体   English

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

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

I don't understand how to use AsyncRestTemplate effectively for making external service calls. 我不明白如何有效地使用AsyncRestTemplate进行外部服务调用。 For the code below: 对于以下代码:

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

Ideally I want to execute all 3 calls simultaneously and process the results once they're all done. 理想情况下,我希望同时执行所有3个调用,并在完成所有操作后处理结果。 However each external service call is not fetched until get() is called but get() is blocked. 但是,在调用get()之前不会获取每个外部服务调用,但get()阻止get() So doesn't that defeat the purpose of AsyncRestTemplate ? 那么这不是打败了AsyncRestTemplate的目的吗? I might as well use RestTemplate . 我不妨使用RestTemplate

So I don't understaand how I can get them to execute simultaneously? 所以我不明白我怎么能让它们同时执行?

Simply don't call blocking get() before dispatching all of your asynchronous calls: 在调度所有异步调用之前,不要调用阻塞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();
  }
}

You can do both dispatch and get in loops, but note that current results gathering is inefficient as it would get stuck on the next unfinished future. 你既可以做调度 ,并在循环中得到的,但要注意,目前的结果是采集效率低下,因为它会卡住的下一个未完成的未来。

You could add all the futures to a collection, and iterate through it testing each future for non blocking isDone() . 您可以将所有期货添加到集合中,并迭代测试每个未来非阻塞isDone() When that call returns true, you can then call get() . 当该调用返回true时,您可以调用get()

This way your en masse results gathering will be optimised rather than waiting on the next slow future result in the order of calling get() s. 这样,您的整体结果收集将被优化,而不是按照调用get() s的顺序等待下一个缓慢的未来结果。

Better still you can register callbacks (runtimes) within each ListenableFuture returned by AccyncRestTemplate and you don't have to worry about cyclically inspecting the potential results. 更妙的是,你可以注册每个内回调(运行时) ListenableFuture由归国AccyncRestTemplate ,你不必担心周期性检查可能的结果。

If you don't have to use 'AsyncRestTemplate' I would suggest to use RxJava instead. 如果您不必使用'AsyncRestTemplate',我建议使用RxJava。 RxJava zip operator is what you are looking for. RxJava zip运算符正是您所需要的。 Check code below: 检查以下代码:

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

All requests to external services will be executed in separate threads, when last call will be finished transformation function( in example simple string concatenation) will be applied and result (concatenated string) will be emmited from 'zip' observable. 所有对外部服务的请求都将在不同的线程中执行,当最后一次调用完成时,将应用转换函数(在示例中简单的字符串连接),结果(连接字符串)将从'zip'可观察到。

What I Understand by Your question is You have a predefined asynchronous method and you try to do is call this method asynchoronously using RestTemplate Class. 我理解你的问题是你有一个预定义的异步方法,你尝试做的是使用RestTemplate类异步调用这个方法。

I have wrote a method that will help you out to call Your method asynchoronously. 我写了一个方法,可以帮助你异步调用你的方法。

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

You might want to use CompletableFuture class ( javadoc ). 您可能想要使用CompletableFuture类( javadoc )。

  1. Transform your calls into CompletableFuture . 将您的调用转换为CompletableFuture For instance. 例如。

     final CompletableFuture<ResponseEntity<String>> cf = CompletableFuture.supplyAsync(() -> { try { return future.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } }); 
  2. Next call CompletableFuture::allOf method with your 3 newly created completable futures. 接下来使用您新创建的3个可完成期货调用CompletableFuture::allOf方法。

  3. Call join() method on the result. 在结果上调用join()方法。 After the resulting completable future is resolved you can get the results from each separate completable future you've created on step 3. 在得到的可完成的未来得到解决后,您可以从步骤3中创建的每个单独的可完成的未来中获得结果。

I think you are misunderstanding a few things here. 我想你在这里误解了一些事情。 When you call the getForEntity method, the requests are already fired. 当您调用getForEntity方法时,请求已被触发。 When the get() method of the future object is called, you are just waiting for the request to complete. 当调用future对象的get()方法时,您只是在等待请求完成。 So in order fire all those three requests on the same subsecond, you just have to do: 因此,为了在同一亚秒内触发所有这三个请求,您只需要:

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

After all these codes are run, all the requests are already fired (most probably in the same subsecond). 运行所有这些代码后,所有请求都已被触发(最可能在同一亚秒内)。 Then you can do whatever you want in the meanwhile. 然后你可以随心所欲地做任何你想做的事。 As soon as you call any of the get() method, you are waiting for each request to complete. 只要调用任何get()方法,就会等待每个请求完成。 If they are already completed, then it will just return immediately. 如果它们已经完成,那么它将立即返回。

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

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. @diginoise响应的问题在于它实际上并没有实现并行性。 As soon as we call get , we're blocked. 一旦我们打电话给get ,我们就被阻止了。 Consider that the calls are really slow such that future1 takes 3 seconds to complete, future2 2 seconds and future3 3 seconds again. 考虑到调用非常慢,因此future1需要3秒才能完成, future2 2秒和future3 3秒再次完成。 With 3 get calls one after another, we end up waiting 3 + 2 + 3 = 8 seconds. 随着3 get的呼声此起彼伏,我们最终等待3 + 2 + 3 = 8秒。 @Vikrant Kashyap answer blocks as well on while (!(future1 .isDone() && future2.isDone() && future3.isDone())) . @Vikrant Kashyap同时回答块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? 除了while循环是一个非常难看的3个期货的代码片段,如果你有更多呢? @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. @lkz答案使用的技术与你要求的不同,即便如此,我也不确定zip是否可以完成这项工作。 From Observable Javadoc: 来自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; zip以严格的顺序应用此函数,因此新Observable发出的第一个项将是应用于每个源Observable发出的第一个项的函数的结果; 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; 新Observable发出的第二个项目将是应用于每个Observable发出的第二个项目的函数的结果; 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. 由于Spring广泛流行,他们非常努力地保持向后兼容性,并且这样做有时会对API做出妥协。 AsyncRestTemplate methods returning ListenableFuture is one such case. 返回ListenableFuture AsyncRestTemplate方法就是这种情况。 If they committed to Java 8+, CompletableFuture could be used instead. 如果他们致力于Java 8+,则可以使用CompletableFuture 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. 由于我们不会直接处理线程池,因此我们无法知道所有ListenableFutures何时完成。 CompletableFuture has an allOf method that creates a new CompletableFuture that is completed when all of the given CompletableFutures complete. CompletableFuture有一个allOf方法,可以创建一个新的CompletableFuture ,在所有给定的CompletableFutures完成时完成。 Since we don't have that in ListenableFuture , we will have to improvise. 由于我们在ListenableFutureListenableFuture ,我们将不得不即兴发挥。 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. 我正在使用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));

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. 现在我们有一个Map ,其中futures.get(Boolean.TRUE)将为我们提供已经完成的所有期货, futures.get(Boolean.FALSE)将为我们提供那些没有完成的futures.get(Boolean.FALSE) 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. 它明确区分了成功案例和失败案例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM