簡體   English   中英

如何使用CompletableFuture並行化Web服務請求?

[英]How to parallelize webservice requests with CompletableFuture?

我有一個Servlet請求,該請求基本上請求輸入日期給出的數據。 由於我有多個日期,因此我必須發送多個請求,然后匯總結果。 例如:

List<Result> results = new ArrayList<>();

for (LocalDate date : dates) {
    ServletReq req = new ServletReq(date);

    try {
        ServletRsp rsp = webservice.send(req);
        results.addAll(rsp.getResults());
    } catch (SpecificException e) {
        //just ignore this result and continue
    }
}

問題:如何並行化上面的代碼? 意思是:發送多個ServletReq異步,並將結果收集到列表中。 等待所有請求完成(可能超時),然后忽略SpecificException

我的開始如下,但是我既不知道這是正確的方向,也沒有成功地完全轉移上面的代碼。 尤其是對於要忽略的異常。

ExecutorService service = Executors.newCachedThreadPool();
List<CompletableFuture<ServletRsp>> futures = new ArrayList<>();

for (LocalDate date : dates) {
    ServletReq req = new ServletReq(date);
    CompletableFuture future = CompletableFuture.supplyAsync(() -> webservice.send(req), service);
    futures.add(future);
}

CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).join();

到目前為止,但是:我如何在異步結果上調用rsp.getResults() ,並將所有內容放入list 在異步執行過程中如何忽略SpecificException (我無法修改webservice.send()方法!)。

  • 在供應商內捕獲它們並返回,例如null 如果您確實對例外情況一無所知,則只能這樣做。 要在future.get()獲得結果,您必須處理nullExecutionException

例如

CompletableFuture<ServletRsp> future = CompletableFuture.supplyAsync(() -> {
    try {
        return webservice.send(new ServletReq(date));
    } catch (SpecificException e) {
        return null;
    }
});
  • 將它們作為(自定義?) RuntimeException重新拋出,這樣就不會丟失它們。 現在,您最終只處理異常,但有些異常是雙重包裝的。
  • 手動完成未來。

例如

CompletableFuture<ServletRsp> future = new CompletableFuture<>();
service.execute(() -> {
    try {
        future.complete(webservice.send(new ServletReq(date));
    } catch (SpecificException e) {
        future.completeExceptionally(e);
    }
});
futures.add(future);

除了ExecutionException之外,沒有其他包裝。 CompletableFuture.supplyAsync做到這一點,但是沒有代碼可以處理已檢查的異常。

  • 只需使用舊的ExecutorService#submit(Callable<T> callable)方法即可,該方法接受拋出的代碼:

例如

List<Callable<String>> tasks = dates.stream()
        .map(d -> (Callable<ServletRsp>) () -> send(new ServletReq(d)))
        .collect(Collectors.toList());
List<Future<ServletRsp>> completed = service.invokeAll(tasks);

我認為您在那兒走的很好。

問題是,除了自己做之外,沒有機制可以很好地收集結果:

ExecutorService service = Executors.newCachedThreadPool();
List<CompletableFuture<Void>> futures = new ArrayList<>(); // these are only references to tell you when the request finishes 
Queue<ServletRsp> results = new ConcurrentLinkedQueue<>(); // this has to be thread-safe

for (LocalDate date : dates) {
    ServletReq req = new ServletReq(date);
    CompletableFuture future = CompletableFuture
            .supplyAsync(() -> webservice.send(req), service)
            .thenAcceptAsync(results::add);
    futures.add(future);
}

CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).join();
// do stuff with results

在您編寫代碼時,我嘗試保留大多數代碼。 也許使用流更干凈:

List<CompletableFuture<Void>> collect = dates
        .map(date -> CompletableFuture
            .supplyAsync(() -> webservice.send(new ServletReq(date)), service)
            .thenAcceptAsync(results::add))
        .collect(Collectors.toList());

    // wait for all requests to finish
    CompletableFuture.allOf(collect.toArray(new CompletableFuture[collect.size()])).thenAcceptAsync(ignored -> {
        //you can also handle the response async.
    });

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM