Огромная задержка в методе CompletableFuture join () - PullRequest
1 голос
/ 13 февраля 2020

Итак, я работаю над приложением, которое должно совершать более 20 HTTP-вызовов одновременно. Каждому из них требуется 2-3 секунды, чтобы получить ответ. Эти вызовы выполняются довольно медленно, в лучшем случае 40 секунд, поэтому я пытаюсь отправить их асинхронно через CompletableFutures. Это должно позволить мне совершать звонки, пока я жду ответа других, теоретически сократив общее время до 4-5 секунд вместо 40.

Я сделал настройку, очень похожую на это руководство найдено по адресу https://www.codepedia.org/ama/how-to-make-parallel-calls-in-java-with-completablefuture-example.

import org.codingpedia.example;

import javax.inject.Inject;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import java.util.stream.Collectors;

public class ParallelCallsDemoService {

    @Inject
    RestApiClient restApiClient;

    private ExecutorService es = Executors.newFixedThreadPool(20);

    public List<ToDo> getToDos(List<String> ids){

        List<CompletableFuture<ToDo>> futures =
                ids.stream()
                          .map(id -> getToDoAsync(id))
                          .collect(Collectors.toList());

        List<ToDo> result =
                futures.stream()
                        .map(CompletableFuture::join)
                        .collect(Collectors.toList());

        return result;
    }


    CompletableFuture<ToDo> getToDoAsync(String id){

        CompletableFuture<ToDo> future = CompletableFuture.supplyAsync(() -> {
            return restApiClient.makeSomeHttpCall(id);
        }, es);

        return future;
    }

}

По всем учетным записям это похоже на работу - все вызовы отправляются примерно в одно и то же время, и все они возвращаются через пару секунд. Но затем я испытываю огромную задержку в 30-40 секунд в этой части:

        List<ToDo> result =
                futures.stream()
                        .map(CompletableFuture::join)
                        .collect(Collectors.toList());

Это занимает примерно столько же времени, сколько и отправка поочередно, что сбивает меня с толку. Как может получиться, что я получаю все ответы за пару секунд, но потом я присоединяюсь к ним на 30 секунд? Это почти как если бы (несмотря на внешность) они все еще делались серийно. Почему объединение занимает так много времени?

Ответы [ 2 ]

0 голосов
/ 19 февраля 2020

Наконец-то понял это! Спасибо всем за ваши предложения. Оказывается, это не имело ничего общего с моей реализацией CompletableFutures. Когда я получаю ответ от службы, я использую JAXB для преобразования объекта java в строку XML для ведения журнала. Я начал смотреть на дампы потоков, когда они зависали, и понял, что это на самом деле преобразование строк JAXB, которого ожидали потоки (объект ответа довольно большой). Я снял эту часть, и производительность сразу улучшилась до того, что должно было быть.

0 голосов
/ 13 февраля 2020

Здесь есть небольшая проблема

List<ToDo> result =
                futures.stream()
                        .map(CompletableFuture::join)
                        .collect(Collectors.toList());

Я думаю, что используемый вами поток не является параллельным потоком. Поэтому каждый вызов на карту ожидает последнего вызова до sh. Изменение futures.stream() на futures.parallelStream() должно иметь улучшение. Конечно, если вы не используете машину с одним ядром.

...