简体   繁体   English

从 java 中的期货列表中收集结果

[英]Collecting results from a list of Futures in java

I'm trying to use futures to make concurrent api calls.我正在尝试使用期货进行并发 api 调用。 Code:代码:

private void init() throws ExecutionException, InterruptedException {
    Long start = System.currentTimeMillis();
    List<ApiResponse> responses = fetchAllUsingFuture(ids, 3);
    log.info(responses.toString());
    Long finish = System.currentTimeMillis();
    log.info(MessageFormat.format("Process duration: {0} in ms", finish-start));
}

private List<ApiResponse> fetchAllUsingFuture(List<String> ids, int threadCount) throws ExecutionException, InterruptedException {
    ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
    List<List<String>> chunks = Utils.splitToChunks(ids, threadCount);
    List<Future<List<ApiResponse>>> futures = new ArrayList<>();
    chunks.forEach(chunk -> {
        futures.add(wrapFetchInFuture(chunk));
    });
    Future<List<ApiResponse>> resultFuture = executorService.submit(() -> {
        List<ApiResponse> responses = new ArrayList<>();
        futures.forEach(future -> {
            try {
                responses.addAll(future.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        });
        return responses;
    });

    executorService.shutdown();
    return resultFuture.get();
}



private Future<List<ApiResponse>> wrapFetchInFuture(List<String> ids) {
    return new FutureTask<>(() -> {
        List<ApiResponse> responses = new ArrayList<>();
        ids.forEach(id -> {
            responses.add(fetchData(id));
        });
        return responses;
    });
}

private ApiResponse fetchData(String id) {
    ResponseEntity<ApiResponse> response = restTemplate.getForEntity(id, ApiResponse.class);
    log.info(MessageFormat.format("Fetching from {0}", id));
    ApiResponse body = response.getBody();
    log.info(MessageFormat.format("Retrieved {0}", body));
    return body;
}

It doesn't execute, the app starts and then just pends.它不执行,应用程序启动,然后只是挂起。 Futures don't get fulfilled.期货没有得到履行。 All advices are appreciated.所有建议表示赞赏。 PS I'm aware this is much more easily done using CompletableFuture, I was just wondering how to do this with Futures PS我知道使用CompletableFuture更容易做到这一点,我只是想知道如何使用Futures做到这一点

In the original version of the question, you are creating a list of FutureTasks but never send them to the ExecutorService to run them.在问题的原始版本中,您正在创建一个FutureTasks列表,但从未将它们发送到ExecutorService来运行它们。 The tasks never complete, so Future.get blocks forever.任务永远不会完成,所以Future.get永远阻塞。

In the updated version of the question, you have put the code that does the waiting into the executor service as a task.在问题的更新版本中,您已将执行等待的代码作为任务放入执行程序服务中。 The FutureTasks never run, so FutureTask.get will still block forever. FutureTasks 永远不会运行,所以FutureTask.get仍然会永远阻塞。

I would suggest you change the code in fetchAllUsingFuture to:我建议您将fetchAllUsingFuture中的代码更改为:

    List<Callable<List<ApiResponse>>> tasks = new ArrayList<>();
    chunks.forEach(chunk -> {
        tasks.add(wrapFetchInCallable(chunk));
    });
    List<Future<List<ApiResponse>>> futures = executorService.invokeAll(tasks);

where wrapFetchInCallable creates a Callable instead of FutureTask :其中wrapFetchInCallable创建一个Callable而不是FutureTask

private static Callable<List<ApiResponse>> wrapFetchInCallable(List<String> ids) {
    return () -> {
        List<ApiResponse> responses = new ArrayList<>();
        ids.forEach(id -> {
            responses.add(fetchData(id));
        });
        return responses;
    };
}

It looks like you are creating a list of FutureTasks but never send them to the ExecutorService to run them.看起来您正在创建一个 FutureTasks 列表,但从未将它们发送到 ExecutorService 来运行它们。 I have implemented ExecutorService with Future Object as below, i hope it helps you:我已经用 Future Object 实现了 ExecutorService,如下所示,希望对您有所帮助:

Service layer:服务层:

public List<MovieDTO> searchMoviesParallel(String limit, String offset, String searchPhrase) throws Exception {

        ExecutorService executor = Executors.newFixedThreadPool(1);
        Future<List<MovieDTO>> digitoonResult = executor.submit(new DigitoonSearchTask(limit, offset, searchPhrase));
        List<MovieDTO> movieDTOList = digitoonResult.get();
        executor.shutdown();

        return movieDTOList;
    }

And my Search task(DigitoonSearchTask class) is as below:我的搜索任务(DigitoonSearchTask 类)如下:

public class DigitoonSearchTask implements Callable<List<MovieDTO>> {

    private String limit;
    private String offset;
    private String searchPhrase;

    private final static String digitoonSearchBaseUrl = "http://apitwo.xxx.com/partner/search/?q=";

    public DigitoonSearchTask(String limit, String offset, String searchPhrase) {
        this.limit = limit;
        this.offset = offset;
        this.searchPhrase = searchPhrase;
    }

    @Override
    public List<MovieDTO> call() throws Exception {
        List<MovieDTO> movieDTOList = new ArrayList<>();
        ObjectMapper mapper = new ObjectMapper();
        try {
            String uri = digitoonSearchBaseUrl + URLEncoder.encode(searchPhrase, "utf-8") + "&limit=" + limit + "&offset=" + offset;
            URL url = new URL(uri);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Accept", "application/json");
            conn.setRequestProperty("authorization", "xxxxxxxxxx");
            if (conn.getResponseCode() != 200) {
                throw new RuntimeException("Failed : HTTP error code : "
                        + conn.getResponseCode());
            }
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    (conn.getInputStream())));

            String output;
            while ((output = br.readLine()) != null) {
                movieDTOList = Arrays.asList(mapper.readValue(output, MovieDTO[].class));
            }
            br.close();
            conn.disconnect();
        } catch (UnknownHostException e) {
            call();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return movieDTOList;
    }}

consider that now I have just one API and after getting others they can be added as another Search task in service layer by increasing the thread number.考虑到现在我只有一个 API 并且在获得其他之后,可以通过增加线程数将它们添加为服务层中的另一个搜索任务。

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

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