CompletableFuture循环:如何收集所有响应并处理错误

时间:2018-07-02 15:32:40

标签: java multithreading asynchronous java-8 completable-future

我试图在循环中为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);
}

2 个答案:

答案 0 :(得分:6)

您可以简单地使用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的条目用于失败的期货,以及另一个带有false密钥的条目用于成功的期货。然后,您可以检查2个条目以采取相应的措施。

请注意,与原始代码相比,有2个细微变化:

  • requestCompletableFuture现在是CompletableFuture<RoomTypes.RoomType>
  • thenApply(responses::add)responses列表已删除

关于日志记录/异常处理,只需添加相关的requestCompletableFuture.handle()即可分别记录它们,但保留requestCompletableFuture而不是handle()产生的记录。

答案 1 :(得分:3)

或者,也许您可​​以从不同的角度解决问题,而不是强制使用ID First Name Last Name 1 Mark Taylor 2 James Bond ,而可以使用JSFiddle

CompletableFuture的整个想法是,一旦对给定未来的答案已经准备好,它将被排入队列,您可以从中使用结果。

替代方法1:没有CompletableFuture

CompletionService

哪种产量:

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

替代2:具有CompletableFuture

但是,如果确实需要处理[One, Two, Three, Five] [java.lang.RuntimeException: Sucks to be four] ,您也可以将它们直接提交到完成服务中,只需将它们直接放入队列中即可。

例如,以下变体具有相同的结果:

CompletableFuture