[英]How to parallelize webservice requests with CompletableFuture?
I have a servlet request that basically requests data given by an input date. 我有一个Servlet请求,该请求基本上请求输入日期给出的数据。 As I have multiple dates, I have to send multiple requests, and then aggregate the results.
由于我有多个日期,因此我必须发送多个请求,然后汇总结果。 For example:
例如:
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
}
}
Question: how can I parallelize the code above? 问题:如何并行化上面的代码? Means: sending multiple
ServletReq
async, and collect the result into the list. 意思是:发送多个
ServletReq
异步,并将结果收集到列表中。 Wait for all requests to finish (maybe with a timeout), and ignore the SpecificException
. 等待所有请求完成(可能超时),然后忽略
SpecificException
。
I started as follows, but neither do I know if this is the right direction, nor did I succeed transfering the code above completely. 我的开始如下,但是我既不知道这是正确的方向,也没有成功地完全转移上面的代码。 Especially regarding the exception to be ignored.
尤其是对于要忽略的异常。
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();
So far, but: How can I call rsp.getResults()
on the async result, and put everything into the list
. 到目前为止,但是:我如何在异步结果上调用
rsp.getResults()
,并将所有内容放入list
。 And how can I ignore the SpecificException
during the async execution? 在异步执行过程中如何忽略
SpecificException
? (I cannot modify the webservice.send()
method!). (我无法修改
webservice.send()
方法!)。
null
. null
。 Only do that if you'd really do nothing with the exception anyways. future.get()
you have to deal with null
and ExecutionException
s. future.get()
获得结果,您必须处理null
和ExecutionException
。 Eg 例如
CompletableFuture<ServletRsp> future = CompletableFuture.supplyAsync(() -> {
try {
return webservice.send(new ServletReq(date));
} catch (SpecificException e) {
return null;
}
});
RuntimeException
so you don't lose them. RuntimeException
重新抛出,这样就不会丢失它们。 Now you deal with just exceptions in the end but some are double-wrapped. Eg 例如
CompletableFuture<ServletRsp> future = new CompletableFuture<>();
service.execute(() -> {
try {
future.complete(webservice.send(new ServletReq(date));
} catch (SpecificException e) {
future.completeExceptionally(e);
}
});
futures.add(future);
No more wrapping besides in ExecutionException
. 除了
ExecutionException
之外,没有其他包装。 CompletableFuture.supplyAsync
does about exactly that, but has no code to deal with checked exceptions. CompletableFuture.supplyAsync
做到这一点,但是没有代码可以处理已检查的异常。
ExecutorService#submit(Callable<T> callable)
method which accepts code that throws: ExecutorService#submit(Callable<T> callable)
方法即可,该方法接受抛出的代码: eg 例如
List<Callable<String>> tasks = dates.stream()
.map(d -> (Callable<ServletRsp>) () -> send(new ServletReq(d)))
.collect(Collectors.toList());
List<Future<ServletRsp>> completed = service.invokeAll(tasks);
I think you're on a good path there. 我认为您在那儿走的很好。
The issue is, that there is no mechanism to nicely collect the results, except doing it yourself: 问题是,除了自己做之外,没有机制可以很好地收集结果:
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
I've tried to keep most of the code as you've written it. 在您编写代码时,我尝试保留大多数代码。 Maybe it's a bit cleaner with streams:
也许使用流更干净:
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.