[英]How to call a method multiple times in parallel using CompletableFuture in Spring Boot
I have a web service in Spring Boot which needs to make requests to an external web service to retrieve live rates for multiple currency pairs. 我在Spring Boot中有一个Web服务,它需要向外部Web服务发出请求以检索多个货币对的实时汇率。
The method that makes the external API call currently looks like this; 当前进行外部API调用的方法如下所示;
@Async
public CompletableFuture<Double> getBaseRateForCurrencyPair(String sourceCurrency, String targetCurrency) {
String key = sourceCurrency.toUpperCase() + targetCurrency.toUpperCase();
Double baseRate = 0.00;
String baseRateProviderUrl = "https://apilayer.net/api/live?access_key=" + currencyLayerAccessKey + "&source=" + sourceCurrency + "¤cies=" + targetCurrency;
HttpClientRequest clientRequest = new HttpClientRequest(baseRateProviderUrl);
try {
Response response = clientRequest.get();
String responseStr = response.body().string();
JSONObject jsonObject = new JSONObject(responseStr);
baseRate = AppHelper.round(jsonObject.getJSONObject("quotes").getDouble(key), 2);
} catch (Exception e) {
e.printStackTrace();
}
return CompletableFuture.completedFuture(baseRate);
}
What I'm trying to do is call this method an unknown number of times depending on how many currency pairs we need to get rates for, wait for all of them to complete and store the results ready to be returned in an API response. 我想做的就是将此方法称为未知次数,具体取决于我们需要获取多少货币对,等待所有货币对完成并存储要在API响应中返回的结果。
I have a method that receives a source currency code, and a list of destination currency rates. 我有一个接收源货币代码和目标货币汇率列表的方法。
public RateResponse getLiveRates(String sourceCurrency, String[] targetCurrencies) {
RateResponse rateResponse = new RateResponse();
rateResponse.setSourceCurrency(sourceCurrency);
/* Loop through target currencies and start a thread for each */
Map<String, CompletableFuture<Double>> ratesMap = new HashMap<>();
for (String targetCurrency : targetCurrencies) {
ratesMap.put(targetCurrency, matrixHelper.getBaseRateForCurrencyPair(sourceCurrency, targetCurrency));
}
/* At this point, I would loop through the Map and grab the results, but only when all are finished */
return rateResponse;
}
My issue is, I can't figure out how to wait until all async calls are finished before I start looping through the Map and retrieving the results. 我的问题是,我无法弄清楚如何等待所有异步调用完成之后才开始遍历Map并检索结果。
A simple solution might look like this: 一个简单的解决方案可能如下所示:
Future
objects in some global set/list Future
对象存储在某些全局集合/列表中 So, your first snippet changes to: 因此,您的第一个代码段更改为:
CompletableFuture rv = CompletableFuture.completedFuture(baseRate);
thatGlobalList.append(rv);
return rv;
And then: 接着:
availableFutures = 0;
while (availableFutures < thatGlobalList.size()) {
availableFutures=0;
for (CompletableFuture future : thatGlobalList) {
if (future.isDone()) {
availableFutures++;
}
Of course, when your map is already built for you, and contains all the future objects, then simply iterate over the values of the map, and sure, all of them are giving you isDone()
! 当然,当您的地图已经为您构建,并且包含所有将来的对象时,只需简单地遍历地图的值即可,并且可以肯定,所有这些都为您提供了
isDone()
!
嗯,也许我在这里错过了一些东西,但是如果您迭代所有可完成对象并调用其CompletableFuture#get,那么根据定义,您将在所有期货完成后完成迭代。
Even though you're using CompletableFuture
, your getBaseRateForCurrencyPair
method returns only after all the processing is done. 即使您使用的是
CompletableFuture
,您的getBaseRateForCurrencyPair
方法也仅在完成所有处理后返回。 To do all that in parallel, you'll have to use something like this: 要并行执行所有这些操作,您将必须使用以下代码:
public CompletableFuture<Double> getBaseRateForCurrencyPair(String sourceCurrency, String targetCurrency) {
CompletableFuture<String> completableFuture
= new CompletableFuture<>();
Executors.newCachedThreadPool().submit(() -> {
String key = sourceCurrency.toUpperCase() + targetCurrency.toUpperCase();
Double baseRate = 0.00;
String baseRateProviderUrl = "https://apilayer.net/api/live?access_key=" + currencyLayerAccessKey + "&source=" + sourceCurrency + "¤cies=" + targetCurrency;
HttpClientRequest clientRequest = new HttpClientRequest(baseRateProviderUrl);
try {
Response response = clientRequest.get();
String responseStr = response.body().string();
JSONObject jsonObject = new JSONObject(responseStr);
baseRate = AppHelper.round(jsonObject.getJSONObject("quotes").getDouble(key), 2);
} catch (Exception e) {
e.printStackTrace();
}
completableFuture.complete(baseRate);
return null;
});
return completableFuture;
}
And to wait for all the tasks to be done, you can use something like this: 为了等待所有任务完成,您可以使用如下代码:
CompletableFuture.allOf(ratesMap.values().stream().toArray(CompletableFuture[]::new))
The you can just iterate the map and use mapValue.get()
to get the Double
value. 您可以仅迭代地图并使用
mapValue.get()
获得Double
值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.