[英]Spring WebFlux - Unable to return HTTP response in HandlerAdapter
[英]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% 确定这是否是这里的问题,但我注意到在使用WebClient
和ParallelFlux
时, WebClient
仅返回响应的Publisher
者( bodyToMono
/ bodyToFlux
),而不是实际请求。
考虑使用Flux.defer
/ Mono.defer
包装远程调用以获取已用于请求的Publisher
者,例如:
.flatMap(url -> Flux.defer(() -> performGetRequest(url)))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.