Реактив Spring: смешивание RestTemplate и WebClient - PullRequest
0 голосов
/ 13 декабря 2018

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

public class Parent {
  private long id;
  private Child child;
}

public class Child {
  private long childId;
  private String someAttribute;
}

Однако вызов /child/{parentId} довольно медленный, поэтому япытаясь сделать это:

  1. Позвоните /parent, чтобы получить 100 родительских данных, используя асинхронный RestTemplate
  2. Для всех родительских данных, вызовите /child/{parentId}, чтобы получить подробную информацию
  3. Добавьте результат вызова к /child/{parentId} в resultList
  4. Когда 100 вызовов к /child/{parentId} завершены, верните resultList

Я использую класс-оболочку, так как большинство конечных точек возвращаетJSON в формате:

{
  "next": "String",
  "data": [
    // parent goes here
  ]
}

Так что я обертываю это в

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

Я написал этот код, но resultList всегда возвращает пустые элементы.Как правильно достичь этого?

public List<Child> getAllParents() {
    var endpointParent = StringUtils.join(HOST, "/parent");
    var resultList = new ArrayList<Child>();

    var responseParent = restTemplate.exchange(endpointParent, HttpMethod.GET, httpEntity,
            new ParameterizedTypeReference<ResponseWrapper<Parent>>() {
            });

    responseParent.getBody().getData().stream().forEach(parent -> {
        var endpointChild = StringUtils.join(HOST, "/child/", parent.getId());
        // async call due to slow endpoint child
        webClient.get().uri(endpointChild).retrieve()
                .bodyToMono(new ParameterizedTypeReference<ResponseWrapper<Child>>() {
                }).map(wrapper -> wrapper.getData()).subscribe(children -> {
                    children.stream().forEach(child -> resultList.add(child));
                });
    });

    return resultList;
}

1 Ответ

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

Вызов subscribe для реактивного типа запускает обработку, но немедленно возвращается;у вас нет гарантии, что обработка завершена.Таким образом, к тому моменту, когда ваш фрагмент вызывает return resultList, WebClient, вероятно, все еще занят извлечением вещей.

Вам лучше отказаться от асинхронного рестайплета (который теперь устарел в пользу WebClient) ипостроить отдельный конвейер, например:

public List<Child> getAllParents() {
    var endpointParent = StringUtils.join(HOST, "/parent");
    var resultList = new ArrayList<Child>();

   Flux<Parent> parents = webClient.get().uri(endpointParent)
            .retrieve().bodyToMono(ResponseWrapper.class)
            .flatMapMany(wrapper -> Flux.fromIterable(wrapper.data));

    return parents.flatMap(parent -> {
      var endpointChild = StringUtils.join(HOST, "/child/", parent.getId());
      return webClient.get().uri(endpointChild).retrieve()
                .bodyToMono(new ParameterizedTypeReference<ResponseWrapper<Child>>() {
                }).flatMapMany(wrapper -> Flux.fromIterable(wrapper.getData()));
    }).collectList().block();
}

По умолчанию оператор parents.flatMap будет обрабатывать элементы с некоторым параллелизмом (я полагаю, 16 по умолчанию)Вы можете выбрать другое значение, вызвав другой вариант оператора Flux.flatMap с выбранным значением параллелизма.

...