PUT
リクエストのループでREST APIを呼び出そうとしています。各呼び出しはCompletableFuture
です。各api呼び出しは、タイプRoomTypes.RoomType
のオブジェクトを返します
さまざまなリストで応答(成功およびエラー応答の両方)を収集したい。どうすればそれを達成できますか?いずれかの呼び出しが更新に失敗すると、すべての結果を取得できないため、allOf
を使用できないと確信しています。
各通話のエラー/例外を記録するにはどうすればよいですか?
public void sendRequestsAsync(Map<Integer, List> map1) {
List<CompletableFuture<Void>> completableFutures = new ArrayList<>(); //List to hold all the completable futures
List<RoomTypes.RoomType> responses = new ArrayList<>(); //List for responses
ExecutorService yourOwnExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
for (Map.Entry<Integer, List> entry :map1.entrySet()) {
CompletableFuture requestCompletableFuture = CompletableFuture
.supplyAsync(
() ->
//API call which returns object of type RoomTypes.RoomType
updateService.updateRoom(51,33,759,entry.getKey(),
new RoomTypes.RoomType(entry.getKey(),map2.get(entry.getKey()),
entry.getValue())),
yourOwnExecutor
)//Supply the task you wanna run, in your case http request
.thenApply(responses::add);
completableFutures.add(requestCompletableFuture);
}
allOf()
を使用して、すべての初期フューチャーが完了したときに(例外の有無にかかわらず)完了したフューチャーを取得し、 Collectors.partitioningBy()
を使用して成功と失敗に分割できます。
List<CompletableFuture<RoomTypes.RoomType>> completableFutures = new ArrayList<>(); //List to hold all the completable futures
ExecutorService yourOwnExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
for (Map.Entry<Integer, List> entry : map1.entrySet()) {
CompletableFuture<RoomTypes.RoomType> requestCompletableFuture = CompletableFuture
.supplyAsync(
() ->
//API call which returns object of type RoomTypes.RoomType
updateService.updateRoom(51, 33, 759, entry.getKey(),
new RoomTypes.RoomType(entry.getKey(), map2.get(entry.getKey()),
entry.getValue())),
yourOwnExecutor
);
completableFutures.add(requestCompletableFuture);
}
CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0]))
// avoid throwing an exception in the join() call
.exceptionally(ex -> null)
.join();
Map<Boolean, List<CompletableFuture<RoomTypes.RoomType>>> result =
completableFutures.stream()
.collect(Collectors.partitioningBy(CompletableFuture::isCompletedExceptionally)));
結果のマップには、失敗したフューチャのtrue
を持つ1つのエントリと、成功したフューチャのfalse
キーを持つ別のエントリが含まれます。その後、2つのエントリを調べて、それに応じて行動できます。
元のコードと比較して2つのわずかな変更があることに注意してください。
requestCompletableFuture
はCompletableFuture<RoomTypes.RoomType>
になりましたthenApply(responses::add)
およびresponses
リストが削除されましたロギング/例外処理に関しては、関連するrequestCompletableFuture.handle()
を追加して個別にログに記録しますが、handle()
の結果ではなくrequestCompletableFuture
を保持します。
または、別の観点から問題にアプローチし、CompletableFuture
の使用を強制する代わりに、代わりに CompletionService を使用できます。
CompletionService
の全体的な考えは、与えられた未来の答えが準備できたらすぐに、結果を消費できるキューに入れられるということです。
代替案1:CompletableFutureなし
CompletionService<String> cs = new ExecutorCompletionService<>(executor);
List<Future<String>> futures = new ArrayList<>();
futures.add(cs.submit(() -> "One"));
futures.add(cs.submit(() -> "Two"));
futures.add(cs.submit(() -> "Three"));
futures.add(cs.submit(() -> { throw new RuntimeException("Sucks to be four"); }));
futures.add(cs.submit(() -> "Five"));
List<String> successes = new ArrayList<>();
List<String> failures = new ArrayList<>();
while (futures.size() > 0) {
Future<String> f = cs.poll();
if (f != null) {
futures.remove(f);
try {
//at this point the future is guaranteed to be solved
//so there won't be any blocking here
String value = f.get();
successes.add(value);
} catch (Exception e) {
failures.add(e.getMessage());
}
}
}
System.out.println(successes);
System.out.println(failures);
どちらが得られますか:
[One, Two, Three, Five]
[Java.lang.RuntimeException: Sucks to be four]
代替案2:CompletableFutureを使用
ただし、CompletableFuture
を本当に処理する必要がある場合は、キューに直接配置するだけで、完了サービスにも送信できます。
たとえば、次のバリエーションは同じ結果になります。
BlockingQueue<Future<String>> tasks = new ArrayBlockingQueue<>(5);
CompletionService<String> cs = new ExecutorCompletionService<>(executor, tasks);
List<Future<String>> futures = new ArrayList<>();
futures.add(CompletableFuture.supplyAsync(() -> "One"));
futures.add(CompletableFuture.supplyAsync(() -> "Two"));
futures.add(CompletableFuture.supplyAsync(() -> "Three"));
futures.add(CompletableFuture.supplyAsync(() -> { throw new RuntimeException("Sucks to be four"); }));
futures.add(cs.submit(() -> "Five"));
//places all futures in completion service queue
tasks.addAll(futures);
List<String> successes = new ArrayList<>();
List<String> failures = new ArrayList<>();
while (futures.size() > 0) {
Future<String> f = cs.poll();
if (f != null) {
futures.remove(f);
try {
//at this point the future is guaranteed to be solved
//so there won't be any blocking here
String value = f.get();
successes.add(value);
} catch (Exception e) {
failures.add(e.getMessage());
}
}
}