使用AsyncRestTemplate多次创建API并等待所有内容完成

TV *_*ath 6 java spring multithreading future asyncresttemplate

我必须使用RestTemplate多次使用不同的参数进行Rest API调用.API是相同的,但它是正在改变的参数.次数也是可变的.我想使用AsyncRestTemplate但我的主线程应该等到所有API调用都成功完成.我还想处理每个API调用返回的响应.目前我正在使用RestTemplate.基本形式如下.

List<String> listOfResponses = new ArrayList<String>();
for (Integer studentId : studentIdsList) {
    String respBody;
    try {
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, method, requestEntity, String.class);
    } catch (Exception ex) {
        throw new ApplicationException("Exception while making Rest call.", ex);
    }
    respBody = requestEntity.getBody();
    listOfResponses.add(respBody);          
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下如何实现AsyncRestTemplate?

Did*_*r L 10

使用AsyncRestTemplate(或实际上是任何异步API)时的主要想法是在第一时间发送所有请求,保留相应的未来,然后再次处理所有响应.您只需使用2个循环即可完成此操作:

List<ListenableFuture<ResponseEntity<String>>> responseFutures = new ArrayList<>();
for (Integer studentId : studentIdsList) {
    // FIXME studentId is not used
    ListenableFuture<ResponseEntity<String>> responseEntityFuture = restTemplate.exchange(url, method, requestEntity, String.class);
    responseFutures.add(responseEntityFuture);
}
// now all requests were send, so we can process the responses
List<String> listOfResponses = new ArrayList<>();
for (ListenableFuture<ResponseEntity<String>> future: responseFutures) {
    try {
        String respBody = future.get().getBody();
        listOfResponses.add(respBody);
    } catch (Exception ex) {
        throw new ApplicationException("Exception while making Rest call.", ex);
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:如果您需要将响应与原始请求配对,则可以使用映射或请求+响应对象列表替换期货列表.

我还注意到studentId你的问题没有使用.

  • “ get()”正在阻止,它只会在请求完成后返回,而其他请求也仍在处理中。因此,如果第二个循环正常完成,则所有请求都将得到处理。 (2认同)
  • 你的问题是“_我的主线程应该等到所有 API 调用都成功完成_”所以这基本上意味着在每个 API 调用完成之前阻塞。重要的一点是请求是并行执行的,因此所需的时间大约是最慢请求的持续时间。 (2认同)

Dan*_*sky 5

如果对您来说可行,您可以使用 Java 8 Stream API:

List<String> listOfResponses = studentIdsList.stream()
    .parrallel()
    .map({studentId ->
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, method, studentId, String.class);
        return responseEntity.getBody();
    })
    .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

这段代码基本上会执行两件事:

  1. 并行执行请求;
  2. 将请求的结果收集到一个列表中。

更新:同意@Didier L - 当您需要执行大量请求时,此解决方案可能无法正常工作。这是一个更新的版本:

List<String> listOfResponses  = studentIdsList.stream()
                .map(studentId -> asyncRestTemplate.exchange(url, method, studentId, String.class)
                .collect(Collectors.toList()).stream()
                .map(this::retrieveResult)
                .collect(Collectors.toList());

    /**
     * Retrieves results of each request by blocking the main thread. Note that the actual request was performed on the previous step when
     * calling asyncRestTemplate.exchange(url, method, studentId, String.class)
     */
    private String retrieveResult(ListenableFuture<ResponseEntity<String>> listenableFuture) {
        try {
            return listenableFuture.get().getBody();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • 它会工作,但它是顺序的,而不是并行的。第一个 `map()` 不会收集所有的期货——你需要一个 `collect()` 调用。相反,第一个请求将被发送(第一个`map()`),然后等待响应(第二个`map()`),然后第二个请求将被发送(第一个`map()`)等等。参见[如何在同一个 Java 流中正确提交和获取多个期货?](/sf/ask/3100900701/ -溪流)。 (2认同)