写点什么

netty 系列之: 在 http2 中使用 framecodec

作者:程序那些事
  • 2021 年 11 月 24 日
  • 本文字数:2401 字

    阅读完需:约 8 分钟

netty系列之:在http2中使用framecodec

简介 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 和 Http2StreamFramenetty 中有三个非常类似的类,他们是 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/


最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!


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

发布于: 2 小时前阅读数: 8
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
netty系列之:在http2中使用framecodec