简介
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/
最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!
欢送关注我的公众号:「程序那些事」,懂技术,更懂你!