繁体   English   中英

如何在Spring Boot中使用CompletableFuture并行调用方法多次

[英]How to call a method multiple times in parallel using CompletableFuture in Spring Boot

我在Spring Boot中有一个Web服务,它需要向外部Web服务发出请求以检索多个货币对的实时汇率。

当前进行外部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 + "&currencies=" + 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);
    }

我想做的就是将此方法称为未知次数,具体取决于我们需要获取多少货币对,等待所有货币对完成并存储要在API响应中返回的结果。

我有一个接收源货币代码和目标货币汇率列表的方法。

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

我的问题是,我无法弄清楚如何等待所有异步调用完成之后才开始遍历Map并检索结果。

您是否尝试过CompletableFuture方法allOf(CompletableFuture<?>... cfs) 在这里寻找文件

一个简单的解决方案可能如下所示:

  • 将创建的Future对象存储在某些全局集合/列表中
  • 然后循环并等待该集合的条目, 然后再执行其他操作

因此,您的第一个代码段更改为:

CompletableFuture rv = CompletableFuture.completedFuture(baseRate);
thatGlobalList.append(rv);
return rv;

接着:

availableFutures = 0;
while (availableFutures < thatGlobalList.size()) {
  availableFutures=0;
  for (CompletableFuture future : thatGlobalList) { 
    if (future.isDone()) {
      availableFutures++;
    }

当然,当您的地图已经为您构建,并且包含所有将来的对象时,只需简单地遍历地图的即可,并且可以肯定,所有这些都为您提供了isDone()

嗯,也许我在这里错过了一些东西,但是如果您迭代所有可完成对象并调用其CompletableFuture#get,那么根据定义,您将在所有期货完成后完成迭代。

即使您使用的是CompletableFuture ,您的getBaseRateForCurrencyPair方法也仅在完成所有处理后返回。 要并行执行所有这些操作,您将必须使用以下代码:

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 + "&currencies=" + 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;
}

为了等待所有任务完成,您可以使用如下代码:

CompletableFuture.allOf(ratesMap.values().stream().toArray(CompletableFuture[]::new))

您可以仅迭代地图并使用mapValue.get()获得Double值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM