繁体   English   中英

Spring WebFlux 执行并行 HTTP 请求并反序列化响应

[英]Spring WebFlux perform parallel HTTP requests and deserialize the response

我有一个包含 URL 的List<String> ,我想对该List中的每个 URL 执行 GET 请求。

这些请求应同时提出。 完成所有请求后,我想要一个List<CustomModel>包含所有反序列化的响应。

所以我创建了一种方法来发出 HTTP 请求

public Flux<JsonNode> performGetRequest(String url) {
    WebClient webClient = WebClient.create(String.format("%s%s", API_BASE_URL, url));

    return webClient.get()
            .retrieve()
            .bodyToFlux(JsonNode.class);
}

上面的方法就是这样调用的

public List<CustomModel> fetch(List<String> urls) {
    return Flux.fromIterable(urls)
            .parallel()
            .runOn(Schedulers.boundedElastic())
            .flatMap(this::performGetRequest)
            .flatMap(jsonNode -> Flux.fromIterable(customDeserialize(jsonNode)))
            .sequential()
            .collectList()
            .flatMapMany(Flux::fromIterable)
            .collectList()
            .block();
}

对于每个响应,我使用自定义方法来反序列化响应

private List<CustomModel> customDeserialize(final JsonNode jsonNodeResponse) {
    List<CustomModel> customModelList = new ArrayList<>();
    
    for (JsonNode block : jsonNodeResponse) {
        // deserialize the response, create an instance of CustomModel class 
        // and add it to customModelList
    }
    
    return customModelList;
}

问题是即使我使用了parallel()方法,整个过程也可能没有并行运行。 完成所需的时间表明我做错了什么。

我错过了什么吗?

问题是即使我使用了 parallel() 方法,整个过程也可能没有并行运行。 完成所需的时间表明我做错了什么。

我错过了什么吗?

由于您正在调用block ,因此我假设您正在运行一个 MVC servlet 应用程序,该应用程序仅将WebClient用于 rest 调用。

如果您没有运行完整的 webflux 应用程序,您的应用程序将启动一个事件循环,该循环将处理所有计划的事件。 如果运行完整的 webflux 应用程序,您将获得与正在运行的机器上的内核一样多的事件循环。

通过使用parallel ,反应堆文档说:

要获得 ParallelFlux,您可以在任何 Flux 上使用parallel()运算符。 就其本身而言,此方法不会并行化工作。 相反,它将工作负载划分为“rails”(默认情况下,rails 的数量与 CPU 内核的数量一样多)。

为了告诉生成的 ParallelFlux 在哪里运行每个轨道(并且,通过扩展,并行运行轨道),您必须使用 runOn(Scheduler)。 请注意,有一个推荐用于并行工作的专用调度程序:Schedulers.parallel()。

您正在创建一个未针对并行工作进行优化的boundedElastic调度程序。

但我想提一下,您正在做的是async i/o而不是parallel工作,这一点非常重要。 当您并行运行时,您很可能不会获得任何性能提升,因为您的大部分 i/o 将触发请求,然后等待响应。

ParellelFlux将确保所有 cpu 核心都被使用,但也有一些惩罚。 有一个设置时间来确保所有内核都起床开始工作,然后需要完成的工作不是 CPU 密集型的,它们只是发出 1000 个请求,然后所有线程都完成了,并且必须等待回复。

需要在核心上设置工人,信息需要发送到每个核心,检索等。

当您进行 CPU 密集型工作时, parallel会获得大部分好处,其中每个事件都需要在多个内核上执行繁重的计算。 但是对于async工作,常规的Flux很可能就足够了。

以下是 reactor 开发人员之一 Simon Baslé 对在 reactor 中运行 i/o 工作(并行与异步)的看法

另外值得一提的是,有boundedElastic调度程序针对阻塞工作进行了调整,作为纯 webflux 应用程序中常规 servlet 行为的后备。

您在 servlet 应用程序中运行 webflux,因此您获得的好处可能不如 webflux 应用程序那么全面。

我不是 100% 确定这是否是这里的问题,但我注意到在使用WebClientParallelFlux时, WebClient仅返回响应的Publisher者( bodyToMono / bodyToFlux ),而不是实际请求。

考虑使用Flux.defer / Mono.defer包装远程调用以获取已用于请求的Publisher者,例如:

.flatMap(url -> Flux.defer(() -> performGetRequest(url)))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM