【问题标题】:Spring reactive : mixing RestTemplate & WebClientSpring 反应式:混合 RestTemplate 和 WebClient
【发布时间】:2019-05-14 09:43:51
【问题描述】:

我有两个端点:/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;
}

【问题讨论】:

    标签: spring-boot spring-webflux project-reactor


    【解决方案1】:

    在反应类型上调用subscribe 会开始处理但立即返回;此时您无法保证处理已完成。所以当你的 sn-p 调用 return resultList 时,WebClient 可能还在忙着取东西。

    您最好放弃异步 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 运算符将处理具有一定并发性的元素(我相信默认情况下为 16 个)。您可以通过使用选定的并发值调用 Flux.flatMap 运算符的另一个变体来选择不同的值。

    【讨论】:

    • 您好,感谢您的回答。它返回列表,但结果是不匹配。 Flux 没有block(),所以我假设我可以使用blockLast()blockFirst()?比方说,我有 10 个父母,每个人有 10 个孩子。现在当我使用blockLast()blockFirst() 时,只有10 个元素从一个父级返回。
    • 要求实际上是:如果对/child 的调用每次花费两秒钟(返回单次调用的 10 个孩子的列表),并且我有 10 个父母,这意味着对/child/parentId 的调用有 10 个,我应该用什么编码来称呼孩子——也许是并行的——并加入那 100 个孩子。但我不必等待 20 秒? (每 2 秒 10 次呼叫)。谢谢
    • 非常感谢。只需对bodyToMono(ResponseWrapper.class) 进行小幅调整。应该是.bodyToMono(new ParameterizedTypeReference&lt;ResponseWrapper&lt;Parent&gt;&gt;()
    猜你喜欢
    • 2020-04-29
    • 1970-01-01
    • 2020-08-26
    • 2019-09-08
    • 2019-04-04
    • 2018-06-07
    • 2020-06-09
    • 1970-01-01
    • 2023-02-05
    相关资源
    最近更新 更多