HTTP/2多路复用需正确组合Http2FrameCodec与Http2MultiplexHandler:前者必须前置解析帧,后者依赖其触发stream创建;ALPN协商失败或配置错误会导致降级至HTTP/1.1;子channel生命周期绑定stream,非TCP连接级;h2c需通过Upgrade流程动态注入编解码器。

HTTP/2多路复用不是“加个Handler”就自动生效的
Netty里开启HTTP/2多路复用,核心不是选对类,而是把Http2FrameCodec和Http2MultiplexHandler按正确顺序、正确上下文组合起来。很多人以为只要加了Http2MultiplexHandler就能自动隔离stream,结果发现多个请求还是串行处理——问题往往出在Http2FrameCodec没前置,或TLS协商没兜住。
-
Http2FrameCodec必须在Http2MultiplexHandler之前,且是pipeline中第一个处理HTTP/2 frame的handler;它负责把原始二进制帧解包成Http2HeadersFrame、Http2DataFrame等对象 -
Http2MultiplexHandler依赖Http2FrameCodec触发stream创建:只有当onHeadersRead被调用,它才基于streamId新建子channel并派发后续frame - 如果漏掉
Http2FrameCodec,Http2MultiplexHandler收不到解析后的headers,stream根本不会诞生,所有frame都卡在父channel里
服务器端ALPN协商失败时,Http2FrameCodec根本不会启动
浏览器只在TLS层通过ALPN声明h2时才走HTTP/2流程;如果ALPN没配好,连接会降级到HTTP/1.1,Http2FrameCodec压根不会被加入pipeline——你代码里写了,但运行时根本没执行。
- 必须用
ApplicationProtocolNegotiationHandler(如ProtocolNegotiationHandler)做协议分发,不能靠if-else手动判断SNI或User-Agent -
configurePipeline里要严格区分ApplicationProtocolNames.HTTP_2和ApplicationProtocolNames.HTTP_1_1,拼错字符串(比如写成"http/2")会导致协议匹配失败 - 自签名证书调试时,客户端需显式信任:
trustManager(InsecureTrustManagerFactory.INSTANCE),否则ALPN阶段就断连,连协商机会都没有
Http2MultiplexHandler的子channel生命周期由stream控制,不是长连接
每个HTTP/2 stream对应一个独立的Channel实例,但它**不是TCP连接级别的channel**,而是逻辑子channel。它的生命周期完全绑定stream:stream关闭(RST_STREAM或正常FIN),子channel就inactive并被回收。别把它当成传统NIO channel去缓存或复用。
- 子channel的
pipeline里不能再加Http2FrameCodec——它只处理本stream的data/header帧,不需要再解帧 - 子channel的
write操作会自动带上当前streamId;但writeAndFlush前必须确保ctx.channel().isActive(),因为stream可能已被对端重置 - 错误日志里出现
StreamException: Stream closed,大概率是往已关闭的子channel发响应,而不是父channel出问题
clear text(h2c)升级需额外处理Upgrade头,和TLS路径完全不同
非TLS环境想用HTTP/2,不能直接上Http2FrameCodec,必须先走HTTP/1.1的Upgrade流程。Netty提供了CleartextHttp2ServerUpgradeHandler封装这步,但它和ALPN路径互斥,混用会导致协议混乱。
- h2c场景下,父channel仍走
HttpRequestDecoder→HttpObjectAggregator→CleartextHttp2ServerUpgradeHandler链路 -
CleartextHttp2ServerUpgradeHandler内部会拦截Upgrade: h2c请求,完成协议切换后,才把Http2FrameCodec注入pipeline——这一步是动态的,不是启动时静态配置 - 一旦升级成功,后续所有数据都走HTTP/2帧;但首次请求仍是HTTP/1.1格式,别在upgrade handler之前就试图读取HTTP/2 frame
真正难的不是写对那几行addLast,而是理解stream和channel的映射关系、ALPN与upgrade的触发时机、以及子channel“即生即灭”的本质。漏掉任意一环,多路复用就退化成单stream串行处理,还查不出错在哪。










