Reactor Netty 1.3.8(2026-10-23)给 HttpServer 加了 maxPipelineDepth(int)。Spring Boot 4.1.2 通过 reactor-bom 2025.0.8 已经带上 1.3.8;4.1.1 还是 1.3.7,反射看不到这个方法。把上限调到 1、接口故意拖 1.5 秒时,同连接三条 HTTP/1.1 流水线请求只会回一条,服务端日志出现 maxPipelineDepth exceeded: 1。

许可证 Apache-2.0。仓库 reactor/reactor-netty。发布说明见 v1.3.8。

1.3.8 多了什么

发布说明里和新的流水线深度相关的条目有两条:

  • HttpServerConfig / HttpServer.maxPipelineDepth(int)(#4380),对接 Netty 4.2.18+ HttpServerCodec 的同名配置
  • HttpCompressionOptionsSpec 也能指定 maxPipelineDepth(#4338)

Javadoc 写的是:在等待响应期间,最多允许解码多少个请求。超出时解码侧抛 IllegalStateException。默认值跟 Netty 一致,是 128。

另外 1.3.8 还依赖 Netty 4.2.19.Final,并加了 WebSocket 服务端指标等改动。本文只核 maxPipelineDepth。

Boot 版本差一档

Maven Central 上:

  • Spring Boot 4.1.1 → reactor-bom 2025.0.7 → reactor-netty-http 1.3.7
  • Spring Boot 4.1.2 → reactor-bom 2025.0.8 → reactor-netty-http 1.3.8

两个进程各自起一个 WebFlux 应用后,启动日志(摘录):

# Boot 4.1.1 / 1.3.7
RN_VERSION_HINT=.../reactor-netty-http-1.3.7.jar!/
HttpServer.maxPipelineDepth_present=false
HttpServer_methods_containing_Pipeline=

# Boot 4.1.2 / 1.3.8
demo.max-pipeline-depth=<default>
RN_VERSION_HINT=.../reactor-netty-http-1.3.8.jar!/
HttpServer.maxPipelineDepth_present=true

fat jar 里也能直接看到工件:4.1.1 打进 reactor-netty-http-1.3.7.jar 和 netty-common-4.2.17.Final.jar;4.1.2 是 1.3.8 和 netty-common-4.2.19.Final.jar。

在 Boot 里怎么接

Boot 4 的类在 org.springframework.boot.reactor.netty 包下。用 WebServerFactoryCustomizer<NettyReactiveWebServerFactory> 调 HttpServer.maxPipelineDepth:

@Bean
WebServerFactoryCustomizer<NettyReactiveWebServerFactory> pipelineDepthCustomizer(
    @Value("${demo.max-pipeline-depth:}") String depth) {
  return factory -> {
    if (depth == null || depth.isBlank()) {
      return;
    }
    int d = Integer.parseInt(depth.trim());
    factory.addServerCustomizers(httpServer -> httpServer.maxPipelineDepth(d));
  };
}

/ping 故意慢一点,让同连接后续请求在等响应时进入解码队列:

@GetMapping("/ping")
Mono<String> ping() {
  int n = hits.incrementAndGet();
  return Mono.delay(Duration.ofMillis(delayMs)).then(Mono.just("pong-138#" + n));
}

启动参数示例:

java -jar rn-138-demo-0.0.1.jar --server.port=8138 --demo.ping-delay-ms=1500
java -jar rn-138-demo-0.0.1.jar --server.port=8139 --demo.max-pipeline-depth=1 --demo.ping-delay-ms=1500

同连接三条流水线

客户端在一条 TCP 连接上连续写出三个 GET /ping(HTTP/1.1 keep-alive),不逐条等响应。命令与日志一致:

python3 pipeline_client.py --port 8138 --count 3
python3 pipeline_client.py --port 8139 --count 3

默认深度(8138)三条都回 200(摘录):

# send 3 pipelined GETs to 127.0.0.1:8138/ping
HTTP/1.1 200 OK
...
pong-138#2
HTTP/1.1 200 OK
...
pong-138#3
HTTP/1.1 200 OK
...
pong-138#4
# bytes=267 status_lines=3

maxPipelineDepth=1(8139)只回一条(摘录):

# send 3 pipelined GETs to 127.0.0.1:8139/ping
HTTP/1.1 200 OK
...
pong-138#2
# bytes=89 status_lines=1

对应服务端 WARN(摘录):

An exception has been observed post termination, use DEBUG level to see the full stack: io.netty.handler.codec.DecoderException: java.lang.IllegalStateException: maxPipelineDepth exceeded: 1

Boot 4.1.1 / 1.3.7 在同样延迟下,三条流水线仍然全回 200,也没有这个配置项可调。

怎么理解这个开关

它限制的是「响应还没写完时,编解码器还能继续解多少个请求」。默认 128,一般业务用不到改。只有你明确知道客户端会在同连接上堆流水线,又希望解码侧更早拒绝,才值得把数字调小。调小之后,超出部分不会悄悄丢掉就算了,连接上会看到解码异常;客户端侧往往只收到已经解完并处理完的那几条响应。

还在 Boot 4.1.1 的,升到 4.1.2 就会带上 1.3.8。不想动 Boot 版本时,也可以单独抬 reactor-netty,但要自己核对和 Boot 管理的 Netty 版本是否合拍;4.1.2 已经对齐到 Netty 4.2.19.Final。

— 感谢阅读 —
广告

一起交流

分享你的思考,让讨论更进一步。