Spring boot Spring被动:混合RestTemplate和WebClient

Spring boot Spring被动:混合RestTemplate和WebClient,spring-boot,spring-webflux,project-reactor,Spring Boot,Spring Webflux,Project Reactor,我有两个端点:/parent和/child/{parentId} 我需要返回所有孩子的名单 但是,对/child/{parentId}的调用非常慢,因此我尝试这样做: 使用异步RestTemplate调用/parent以获取100个父数据 对于每个父数据,调用/child/{parentId}获取详细信息 将对/child/{parentId}的结果调用添加到resultList中 完成对/child/{parentId}的100个调用后,返回resultList 我使用包装类,因为大多数端点以

我有两个端点:/parent和/child/{parentId} 我需要返回所有孩子的名单

但是,对/child/{parentId}的调用非常慢,因此我尝试这样做:

使用异步RestTemplate调用/parent以获取100个父数据 对于每个父数据,调用/child/{parentId}获取详细信息 将对/child/{parentId}的结果调用添加到resultList中 完成对/child/{parentId}的100个调用后,返回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;
}
对反应类型调用subscribe启动处理,但立即返回;此时您无法保证处理已完成。因此,当您的代码片段调用ReturnResultList时,WebClient可能仍在忙于获取内容

您最好放弃async resttemplate,它现在已被弃用,取而代之的是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操作符将处理具有某种并发性的元素。您可以通过调用具有所选并发值的Flux.flatMap操作符的另一个变体来选择不同的值。

您好,谢谢您的回答。它返回列表,但结果不匹配。通量没有阻塞,所以我假设我可以使用blockLast或blockFirst?我有10个父母,每个人都有10个孩子。现在,当我使用blockLast或blockFirst时,只有10个元素从一个父级返回。实际上,要求是:如果对/child的调用每次花费两秒钟来返回10个子级的列表,并且我有10个父级,这意味着对/child/parentId的调用有10个,那么我应该编写什么代码来调用该子级(可能是并行的)并加入这100个子级。但我不必等20秒?每2秒钟打10个电话。非常感谢你。只需对bodyToMonoResponseWrapper.class进行少量调整。它应该是.bodyToNoNew ParameterizedTypeReference
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();
}