简体   繁体   English

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

[英]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 + "&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);
    }

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并检索结果。

Have you tried CompletableFuture method allOf(CompletableFuture<?>... cfs) ? 您是否尝试过CompletableFuture方法allOf(CompletableFuture<?>... cfs) Look for docs here 在这里寻找文件

A simple solution might look like this: 一个简单的解决方案可能如下所示:

  • store the created Future objects in some global set/list 将创建的Future对象存储在某些全局集合/列表中
  • to then loop and wait on the entries of that set, before doing anything else 然后循环并等待该集合的条目, 然后再执行其他操作

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

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.

相关问题 如何使用 CompletableFuture 并行运行多个服务调用? - How to run multiple service calls in parallel using CompletableFuture? 如何在 Spring Boot 中缓存 CompletableFuture 的值 - How to cache the value of a CompletableFuture in Spring Boot 使用 spring boot # SOAP 服务调用多个并行调用 SOAP 服务 - Calling multiple parallel call to SOAP services using spring boot # SOAP Service 如何使用 RxJava 的方法 retryWhen 多次调用一个方法? - How to call a method multiple times using RxJava's method retryWhen? 如果使用 CompletableFuture 失败,如何并行调用独立函数并抛出异常 - How to call independent functions parallel and throw exception if one fails using CompletableFuture 如何使用 CompletableFuture 将此代码转换为 Java 8 中的异步代码? 我正在使用 Spring 引导 - How to convert this code to async in Java 8 using CompletableFuture ? I am using Spring Boot 如何在另一个Jar中多次调用具有另一个Jar中的参数的Java类 - How to call a Java Class with parameters that is in another Jar, multiple times in Parallel 将CompletableFuture与@Async一起使用将为Spring Boot API返回空响应 - Using CompletableFuture with @Async returns an empty response for spring boot API 如何多次调用方法来更改变量? - How to call a method multiple times to change a variable? 如何多次调用语音方法 - how to call speak method multiple times
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM