简介

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/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」,懂技术,更懂你!