写点什么

netty 系列之:netty 实现 http2 中的流控制

发布于: 刚刚

简介

HTTP2 相对于 http1.1 来说一个重要的提升就是流控制 flowcontrol。为什么会有流控制呢?这是因为不管是哪种协议,客户端和服务器端在接收数据的时候都有一个缓冲区来临时存储暂时处理不了的数据,但是缓冲区的大小是有限制的,所以有可能会出现缓冲区溢出的情况,比如客户端向服务器端上传一个大的图片,就有可能导致服务器端的缓冲区溢出,从而导致一些额外的数据包丢失。

为了避免缓冲区溢出,各个 HTTP 协议都提供了一定的解决办法。

在 HTTP1.1 中,流量的控制依赖的是底层 TCP 协议,在客户端和服务器端建立连接的时候,会使用系统默认的设置来建立缓冲区。在数据进行通信的时候,会告诉对方它的接收窗口的大小,这个接收窗口就是缓冲区中剩余的可用空间。如果接收窗口大小为零,则说明接收方缓冲区已满,则发送方将不再发送数据,直到客户端清除其内部缓冲区,然后请求恢复数据传输。

HTTP2 通过客户端和服务器端的应用中进行缓冲区大小消息的传输,通过在应用层层面控制数据流,所以各个应用端可以自行控制流量的大小,从而实现更高的连接效率。

本文将会介绍 netty 对 http2 流控制的支持。

http2 中的流控制

在简介中我们也提到了,传统的 HTTP1.1 使用的是系统底层的流量控制机制,具体来说就是 TCP 的流控制。但是 TCP 的流控制在 HTTP2 中就不够用了。因为 HTTP2 使用的是多路复用的机制,一个 TCP 连接可以有多个 http2 连接。所以对 http2 来说 TCP 本身的流控制机制太粗糙了,不够精细。

所以在 HTTP2 中,实现了更加精细的流控制机制,它允许客户端和服务器实现其自己的数据流和连接级流控制。

具体的流程是这样的,当客户端和服务器端建立连接之后,会发送 Http2SettingsFrame,这个 settings frame 中包含了 SETTINGS_INITIAL_WINDOW_SIZE,这个是发送端的窗口大小,用于 Stream 级别流控。流控制窗口的默认值设为 65,535 字节,但是接收方可以对其进行修改,最大值为 2^31-1 字节。

建立好初始 windows size 之后,对于接收方来说,每次发送方发送 data frame 就会减少 window 的的大小,而接收方每次发送 WINDOW_UPDATE frame 时候就会增加 window 的大小,从达到动态控制的目的。

netty 对 http2 流控制的封装

Http2FlowController

从上面的介绍我们知道,http2 对流控制是通过两个方面来实施的,第一个方面就是初始化的 Http2SettingsFrame,通过设置 SETTINGS_INITIAL_WINDOW_SIZE 来控制初始 window 的大小。第二个方面就是在后续的 WINDOW_UPDATE frame 中对 window 的大小进行动态增减。

对于 netty 来说,这一切都是封装在 Http2FlowController 类中的。Http2FlowController 是一个抽象类,它有两个实现,分别是 Http2LocalFlowController 和 Http2RemoteFlowController。他们分别表示对 inbound flow of DATA 和 outbound flow of DATA 的处理。

Http2FlowController 中主要有 5 个方法,分别是:

  • set channelHandlerContext:绑定 flowcontrol 到 ChannelHandlerContext 上。

  • set initialWindowSize:初始化 window size,等同于设置 SETTINGS_INITIAL_WINDOW_SIZE。

  • get initialWindowSize: 返回初始化 window size。

  • windowSize: 获取当前的 windowSize。

  • incrementWindowSize: 增加 flow control window 的大小。

接下来我们看下他的两个实现类,有什么不一样的地方。

Http2LocalFlowController

LocalFlowController 用来对远程节点发过来的 DATA frames 做 flow control。它有 5 个主要的方法。

  • set frameWriter: 用来设置发送 WINDOW_UPDATE frames 的 frame writer。

  • receiveFlowControlledFrame: 接收 inbound DATA frame,并且对其进行 flow control。

  • consumeBytes: 表示应用已经消费了一定数目的 bytes,可以接受更多从远程节点发过来的数据。flow control 可以发送 WINDOW_UPDATE frame 来重置 window 大小。

  • unconsumedBytes: 接收到,但是未消费的 bytes。

  • initialWindowSize: 给定 stream 的初始 window 大小。

Http2RemoteFlowController

remoteFlowController 用来处理发送给远程节点的 outbound DATA frames。它提供了 8 个方法:

  • get channelHandlerContext: 获取当前 flow control 的 context.

  • addFlowControlled: 将 flow control payload 添加到发送到远程节点的 queue 中。

  • hasFlowControlled: 判断当前 stream 是否有 FlowControlled frames 在 queue 中。

  • writePendingBytes: 将流量控制器中的所有待处理数据写入流量控制限制。

  • listener: 给 flow-controller 添加 listener。

  • isWritable: 确定流是否有剩余字节可用于流控制窗口。

  • channelWritabilityChanged: context 的 writable 状态是否变化。

  • updateDependencyTree: 更新 stream 之间的依赖关系,因为 stream 是可以有父子结构的。

流控制的使用

flowControl 相关的类主要被用在 Http2Connection,Http2ConnectionDecoder,Http2ConnectionEncoder 中,在建立 http2 连接的时候起到相应的作用。

总结

flowControl 是 http2 中的一个比较底层的概念,大家在深入了解 netty 的 http2 实现中应该会遇到。

本文已收录于 http://www.flydean.com/29-netty-flowcontrol/

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

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

发布于: 刚刚阅读数: 2
用户头像

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

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

评论

发布
暂无评论
netty系列之:netty实现http2中的流控制