Эффективный способ сделать асинхронный вызов в Spring REST - PullRequest
0 голосов
/ 14 декабря 2018

У меня есть две конечные точки: /parent и /child/{parentId} Обе вернут список

Предположим, что каждый вызов займет две секунды.Поэтому, если я позвоню /parent и у меня будет 10 родителей в списке, и я хочу позвонить и заполнить каждого ребенка, мне понадобится всего 22 секунды (2 секунды для /parent, 10 раз /child/{parentId} с 2 секундами для каждого)

В Spring и Java 10 я могу использовать RestTemplate в сочетании с Future для выполнения асинхронного вызова.
В этом фрагменте /slow-five - это вызов родителя, а /slow-six - вызовchild.

public List<Child> runSlow2() {
    ExecutorService executor = Executors.newFixedThreadPool(5);

    long start = System.currentTimeMillis();
    RestTemplate restTemplate = new RestTemplate();
    var futures = new ArrayList<Future<List<Child>>>();
    var result = new ArrayList<Child>();

    System.out.println("Start took (ms) : " + (System.currentTimeMillis() - start));
    var responseFive = restTemplate.exchange("http://localhost:8005/api/r/slow-five", HttpMethod.GET, null,
            new ParameterizedTypeReference<ResponseWrapper<Parent>>() {
            });

    for (var five : responseFive.getBody().getData()) {
        // prepare future
        var future = executor.submit(new Callable<List<Child>>() {

            @Override
            public List<Child> call() throws Exception {
                var endpointChild = "http://localhost:8005/api/r/slow-six/" + five.getId();

                var responseSix = restTemplate.exchange(endpointChild, HttpMethod.GET, null,
                        new ParameterizedTypeReference<ResponseWrapper<Child>>() {
                        });

                return responseSix.getBody().getData();
            }
        });

        futures.add(future);
    }

    for (var f : futures) {
        try {
            result.addAll(f.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    System.out.println("Before return took (ms) : " + (System.currentTimeMillis() - start));

    return result;
}

Игнорировать оболочку ResponseWrapper.Это просто класс-оболочка, подобный этому

public class ResponseWrapper<T> {
    private List<T> data;
    private String next;
}

Код работает нормально, для сбора всех детей от 10 родителей потребовалось около 3-4 секунд.Но я не думаю, что это эффективно.
Более того, у Spring 5 есть WebClient, который должен уметь делать подобные вещи.
Однако я не могу найти ни одного примера для такого рода иерархических вызовов.Большинство примеров на WebClient включает в себя только простой вызов одной конечной точки без зависимости.
Любая подсказка, как я могу использовать WebClient для достижения тех же целей?Вызовите несколько /child асинхронно и объедините результат?

Спасибо

1 Ответ

0 голосов
/ 14 декабря 2018

Потребовалось около 3-4 секунд, чтобы собрать всех детей от 10 родителей.


Я думаю, мы должны прояснить, что замедляет метод runSlow2 ().
Ваш метод делает несколько звонков на конечные точки.Вы улучшаете производительность, выполняя параллелизм вызовов и собирая результаты из них.
Я не думаю, что restTemplate медленный, ничего плохого в вашем коде, может быть, ваши конечные точки медленные.
Одним из улучшений может быть вместо параллельного выполнения нескольких вызовов / child / {parentId}, вы можете ввести новыйконечная точка, которая принимает список parentId.Надеюсь, это поможет.

...