简介
netty 为咱们提供了很多 http2 的封装,让咱们能够轻松的搭建出一个反对 http2 的服务器。其中惟一须要咱们自定义的就是 http2 handler。
在之前的文章中,咱们介绍了自定义 http2handler 继承自 Http2ConnectionHandler 并且实现 Http2FrameListener。这种实现形式是 netty 目前比拟举荐的实现形式,明天给大家介绍的一种实现形式是 netty 中筹备替换继承 Http2ConnectionHandler 的实现形式,然而这种实现形式并不成熟,还在一直的欠缺中。
明天给大家介绍一下这种实现形式。
Http2FrameCodec
这种实现形式的外围类是 Http2FrameCodec。事实上 Http2FrameCodec 也是继承自 Http2ConnectionHandler。
它的次要作用是将 HTTP/ 2 中的 frames 和 Http2Frame 对象进行映射。Http2Frame 是 netty 中对应所有 http2 frame 的封装,这样就能够在后续的 handler 中专一于解决 Http2Frame 对象即可,从而解脱了 http2 协定的各种细节,能够缩小使用者的工作量。
对于每个进入的 HTTP/2 frame,Http2FrameCodec 都会创立一个 Http2Frame 对象,并且将其传递给 channelRead 办法,用于对该对象进行解决。
通过调用 write 办法,能够对 outbound 的 Http2Frame 转换成为 http2 frame 的格局。
Http2Frame、Http2FrameStream 和 Http2StreamFrame
netty 中有三个十分相似的类,他们是 Http2Frame、Http2FrameStream 和 Http2StreamFrame。
咱们晓得 netty 中一个 tcp 连贯能够建设多个 stream,Http2FrameStream 就是和 stream 对应的类,这个类中蕴含了 stream 的 id 和 stream 以后的状态。
一个 stream 中又蕴含了多个音讯,每个音讯都是由多个 frame 组成的,所以 Http2Frame 是和这些 frame 对应的 netty 类。
Http2StreamFrame 自身也是一个 frame,事实上它继承自 Http2Frame。
为什么会有这个类呢?因为对应 frame 自身来说,个别状况下它是和一个特定的 stream 相关联的,Http2StreamFrame 示意这种关联关系,能够通过它的 set stream 办法来指定其关联的 stream。如果想要该 frame 利用到整个连贯而不是特定的某个 stream,如果是关联到整个连贯,那么 stream() 办法的返回就是 null。
Http2FrameCodec 的结构
尽管 Http2FrameCodec 有构造函数,然而 netty 举荐应用 Http2FrameCodecBuilder 来结构它:
Http2FrameCodecBuilder.forServer().build();
能够看到 Http2FrameCodecBuilder 有一个 forServer 还有一个 forClient 办法。他们一个是应用在服务器端,一个是应用在客户端。
次要是通过外面的 server 属性来进行辨别。
Stream 的生命周期
frame codec 将会向无效的 stream 发送和写入 frames。之前讲过了 Http2StreamFrame 是和一个 Http2FrameStream 对象相关联的。
对于一个无效的 stream 来说,如果任意一方发送一个 RST_STREAM frame,那么该 stream 就会被敞开。
或者发送方或者接管方任意一方发送的 frame 中带有 END_STREAM 标记,该 stream 也会被敞开。
流控制
Http2FrameCodec 提供了对流的自动控制,然而咱们依然须要做一些操作,来对 window 进行更新。
具体而言,当咱们在接管到 Http2DataFrame 音讯的时候,对音讯进行解决之后,须要增大 window 的大小,示意该 data 曾经被解决了,能够有更多的空间去包容新的数据。
也就是说须要向 ctx 中写入一个 Http2WindowUpdateFrame,在这个 Http2WindowUpdateFrame 中须要传入解决的 data 的大小和对应 stream 的 id,上面是一个解决 data frame 的例子:
/**
* 解决 data frame 音讯
*/
private static void onDataRead(ChannelHandlerContext ctx, Http2DataFrame data){Http2FrameStream stream = data.stream();
if (data.isEndStream()) {sendResponse(ctx, stream, data.content());
} else {
// 不是 end stream 不发送,然而须要开释援用
data.release();}
// 解决完 data,须要更新 window frame,减少解决过的 Data 大小
ctx.write(new DefaultHttp2WindowUpdateFrame(data.initialFlowControlledBytes()).stream(stream));
}
上的例子中,咱们向 DefaultHttp2WindowUpdateFrame 传入了对应的 stream id,如果 stream id 为 0,则示意解决的是整个 connection,而不是独自的某个 stream。
除了 window update frame 之外,对于某个特定 stream 的初始 window 还能够发送一个 Http2SettingsFrame,通过设置 Http2Settings.initialWindowSize() 达到初始化的目标。
接管音讯
对于每个 HTTP/2 stream 来说,其中蕴含的 frame 能够分为 Http2HeadersFrame 和 Http2DataFrame,而 Http2HeadersFrame 必然是第一个接管到的 frame, 并且这个 headerFrame 还关联了对应的 stream 对象:Http2FrameStream。
所以咱们在解决的时候能够针对这两种不同的 frame 别离进行解决:
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {if (msg instanceof Http2HeadersFrame) {onHeadersRead(ctx, (Http2HeadersFrame) msg);
} else if (msg instanceof Http2DataFrame) {onDataRead(ctx, (Http2DataFrame) msg);
} else {super.channelRead(ctx, msg);
}
}
自定义 handler
如果应用 Http2FrameCodec,咱们只须要在 pipline 中增加 Http2FrameCodec 之后,再增加自定义的 handler 即可:
ctx.pipeline().addLast(Http2FrameCodecBuilder.forServer().build(), new CustHttp2Handler());
因为 Http2FrameCodec 曾经对 http2 中的 frame 进行了转换,所以咱们在 CustHttp2Handler 中只须要解决自定义逻辑即可。
netty 举荐自定义的 handler 继承自 Http2ChannelDuplexHandler,因为它比一般的 ChannelDuplexHandler 多了一个创立 newStream() 和遍历所有无效 stream 的 forEachActiveStream(Http2FrameStreamVisitor) 办法。
总结
本文解说了 Http2FrameCodec 的原理,和与其搭配的 handler 实现中要留神的事项。
本文的例子能够参考:learn-netty4
本文已收录于 http://www.flydean.com/31-netty-framecodec-http2/
最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!
欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!