異なるパラメーターでRestTemplateを複数回使用してRestAPI呼び出しを行う必要があります。 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);
}
この状況でAsyncRestTemplateを実装するにはどうすればよいですか?
AsyncRestTemplate
(または実際には非同期API)を使用する場合の主なアイデアは、すべてのリクエストを最初に送信し、対応する先物を保持してから、すべての応答を2回目に処理することです。これは、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);
}
}
注:応答を元の要求とペアにする必要がある場合は、先物のリストをマップまたは要求+応答オブジェクトのリストに置き換えることができます。
また、あなたの質問ではstudentId
が使用されていないことにも注意しました。
可能であれば、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());
このコードは基本的に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();
}
}
AsyncRestTemplateではなくSpringのRestTemplateを使用する別の解決策を提案したいと思います。また、Java 8CompletableFutureを使用しています。
public void sendRequestsAsync(List<Integer> studentList) {
List<CompletableFuture<Void>> completableFutures = new ArrayList<>(studentList.size()); //List to hold all the completable futures
List<String> responses = new ArrayList<>(); //List for responses
ExecutorService yourOwnExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
for (Integer studentId : studentList) { //Iterate student list
CompletableFuture<Void> requestCompletableFuture = CompletableFuture
.supplyAsync(
() -> restTemplate.exchange("URL/" + studentId, HttpMethod.GET, null, String.class),
yourOwnExecutor
)//Supply the task you wanna run, in your case http request
.thenApply((responseEntity) -> {
responses.add(responseEntity.getBody());
return responseEntity;
})//now you can add response body to responses
.thenAccept((responseEntity) -> {
doSomeFinalStuffWithResponse(responseEntity);
})//here you can do more stuff with responseEntity (if you need to)
.exceptionally(ex -> {
System.out.println(ex);
return null;
});//do something here if an exception occurs in the execution;
completableFutures.add(requestCompletableFuture);
}
try {
CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[completableFutures.size()])).get(); //Now block till all of them are executed by building another completablefuture with others.
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
私はこのソリューションがもっと好きです。なぜなら、必要なだけビジネスロジックをチェーンでき、非同期送信のためにSpringの内部に依存する必要がないからです。明らかに、コードをさらにクリーンアップすることができますが、今のところあまり注意を払っていません。