官方 JDK 28 Early-Access Build 21(2026/10/22)已经放出。昨天那篇 HttpClient HTTP/2 ofInputStream 晚读丢 写的是:公开 EA 还停在 b20,修复目标是 b21。今天用官网 Linux/x64 包 28-ea+21-1707 跑同一套复现:ofInputStream() 晚读 300ms 后能读满 8000 字节;同机对照的 28-ea+20-1588 仍是 IOException: closed。
JDK-8394607 的修复已经进官方 EA,不必再依赖本地 internal 构建去验证。
测了哪两个包
两个都是 jdk.java.net 的 GPL Early-Access,不是自编译:
# 28-ea+21(Build 21,2026/10/22)
openjdk version "28-ea" 2027-03-23
OpenJDK Runtime Environment (build 28-ea+21-1707)
OpenJDK 64-Bit Server VM (build 28-ea+21-1707, mixed mode, sharing)
SOURCE=".:git:b6654fc389e8"
# 对照:28-ea+20
openjdk version "28-ea" 2027-03-23
OpenJDK Runtime Environment (build 28-ea+20-1588)
OpenJDK 64-Bit Server VM (build 28-ea+20-1588, mixed mode, sharing)
JDK-8394607 的 Resolved In Build 写的是 b21。b20→b21 的变更列表里能看到提交 5c1c7c1(HttpClient HTTP/2:对端关连接前已收齐的 body 被丢掉)。对 java.net.http 模块里的 Stream.class 做 javap -p:b21 有私有方法 getBody(BodySubscriber),b20 没有。
同一套复现,b21 过了
场景不变:Node 起一个只谈 h2 的 TLS 服务,发 8 帧 × 1000 字节(最后一帧 END_STREAM),再 GOAWAY(NO_ERROR) 关连接;Java 客户端用 BodyHandlers.ofInputStream(),拿到响应后 sleep 300ms 再 readAllBytes()。复现材料还是 bug 页上的那几份。
官方 28-ea+21-1707:
=== LABEL=jdk28ea21 MODE=inputstream ===
openjdk version "28-ea" 2027-03-23
OpenJDK Runtime Environment (build 28-ea+21-1707)
PORT=46005
VERSION=HTTP_2
BYTES=8000
SUCCESS=true
EXIT=0
BODY_SENT=8000
GOAWAY_SENT=NO_ERROR
SESSION_CLOSED=true
同机立刻再跑 28-ea+20-1588:
=== LABEL=jdk28ea20 MODE=inputstream ===
openjdk version "28-ea" 2027-03-23
OpenJDK Runtime Environment (build 28-ea+20-1588)
PORT=41601
VERSION=HTTP_2
TARGET_FAILURE=java.io.IOException: closed
Caused by: java.io.EOFException: EOF reached while reading
EXIT=2
BODY_SENT=8000
GOAWAY_SENT=NO_ERROR
SESSION_CLOSED=true
服务端两边都发完了 8000 字节。差别只在客户端构建号。
b21 上另外两个对照也正常:
# ofByteArray
VERSION=HTTP_2
BYTES=8000
SUCCESS=true
EXIT=0
# ofInputStream 立刻读(control)
VERSION=HTTP_2
BYTES=8000
SUCCESS=true
EXIT=0
和昨天那篇怎么接
机理、回归点(JDK-8317808)、以及 21.0.12.1 / 27+35 上的失败矩阵,昨天文已经写过,这里不复读。昨天用本地 28-internal(SOURCE=bf83d326d844)证明过主线修是有效的;今天补的是:官方公开 EA 已经把这一笔打进 Build 21,下载页就是证据。
生产若还停在 21.0.12.1、27+35、28-ea+20 这类未带 5c1c7c1 的构建,临时规避还是那几条:强制 HTTP/1.1、改用 ofByteArray()/ofString()、或者拿到流立刻读。要验官方修复,直接装 28-ea+21 即可。21u / 25u 会不会把同一笔带回,得另盯 backport,本文只测了官方 28 EA 这两个 build。
一起交流
分享你的思考,让讨论更进一步。