我们在使用 Spring Boot 2.0、Webflux 5.0.7 和 Netty 4.1.25 时遇到网络传输问题。我们希望将 100000 个序列化为 JSON(大约 10Mb 的网络流量)的项目传输到 1 个客户端。
NIO 和传统 IO 之间的网络传输性能有很大的不同。
测试结果如下:
Start reading 100000 from server in 5 iterations
Avg HTTP 283 ms
Avg stream 8130 ms
目前每秒 请求的数量不是问题 ,但网络传输速度是。我们已经读到,在网络速度方面,NIO 可能会慢 30% 左右,但 1/30 倍是过大的。
在客户端和服务器端采样时,我们观察到原因主要在于服务器端实现。从下面的截图可以看出,服务器大部分时间都花在方法
select()
和 doWrite()
上。端点代码本身:
@RestController
@RequestMapping(produces = {APPLICATION_JSON_VALUE, APPLICATION_STREAM_JSON_VALUE})
@Validated
public class StreamingController {
@GetMapping("/instruments/{eodDate}")
public Flux<TestItem> getInstruments(
@PathVariable @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate eodDate,
@RequestParam(required = false) Instant asOfTimestamp) {
//Generate test data in memory
List<TestItem> collect = IntStream.range(0, 100000)
.mapToObj(i -> new TestItem.Builder().build())
.collect(Collectors.toList());
return Flux.fromIterable(collect);
}
}
我们正在为 Netty 使用 Spring Boot 配置,我们怀疑默认情况下 Netty 配置错误。
我们正在寻求您的帮助。我会根据您的要求添加任何其他详细信息。
更新:
目标是批量读取整个响应以避免将所有响应放入内存中,因为预期的数据量很大(几 Gb)。在客户端消费一批数据而不是一个元素是可以接受的。
最佳答案
您实际上并不是在测试 NIO 与 IO。 Spring WebFlux 应用程序始终在服务器级别使用非阻塞 IO(使用 Netty、Undertow 或任何 Servlet 3.1+ 异步 IO 兼容服务器)。
在这种情况下,您正在比较:
"application/json"
有效负载 "application/stream+json"
响应 在第一种情况下,Spring WebFlux 以 react 方式生成响应主体,但将缓冲和刷新决策留给服务器本身。写入网络是有成本的,缓冲 a 但是写入更大的块是有效的。
在第二种情况下,您要求 Spring WebFlux 为
Flux
的每个元素编写和刷新 。当客户端正在监听(可能是无限的)事件流并且两个不同的事件之间可能存在一些时间时,这很有用。该方法消耗更多资源并解释了性能差异。所以这个基准测试不是显示 IO 与 NIO,而是流式与非流式。
如果您想对响应写入/刷新进行细粒度控制,您可以下降到
ServerHttpResponse
级别并使用 writeAndFlushWith(Flux<Flux<DataBuffer>>)
,但这是相当低的级别,因为您直接处理 DataBuffer
实例。另一种方法是创建包含
TestItem
列表的中间 JSON 对象,例如:public Flux<TestItemBatch> batch() {
Flux<TestItem> items= //...;
Flux<List<TestItem>> itemsLists = items.buffer(100);
return itemsLists.map(list -> new TestItemBatch(list));
}
关于java - Webflux + Netty NIO 性能比传统 IO 降低约 30 倍,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52837041/