问答 百科手机端

如何定位并修复 HttpCore5 中的 HTTP2 流量控制问题

2022-02-26 16:53

开篇吹一波阿里云性能测试服务 PTS[1],PTS 在 2021 年 5 月份已经上线了对 协议。

背景写这篇文章的原因是某天某个客户找过来,问我们是不是不支持 的接口总是报错:

起初怀疑是 引擎侧的问题。

通过本地 debug,看到是因为请求 URL 时,客户端窗口大小被调整为大于 2^32 -1 导致的异常。

那正好借这个机会看下这里的窗口大小指的是什么。

引入了流和多路复用,通过流控可以达到使多个流协同的效果。

一些流控的基本概念:

流控是针对连接而言的,不是针对端到端的,而是在两端中的每一跳;主要指有代理的情况下,代理与两端都存在流控流控是基于WINDOW_UPDATE 帧的,接收者可以通过流控控制发送者的速度流控既可以作用于 stream 也可以作用于 connection对于连接与所有新开启的流而言,流控窗口大小默认都是 65535,且最大值为 2^32 - 1流控无法禁用为了便于理解,先简单列一下 帧的类型:

DATA:携带请求或响应中的数据HEADERS:用于新建一个流(请求或响应),包含对应的 HeadersPRIORITY:用于配置流的优先级RST_STREAM:强制结束某个流,仅用于某一端取消流,并不适用于正常流的结束SETTINGS:H2 建联的一些配置PUSH_PROMISE:服务端推送响应到客户端PING:向远端发送一条 PING,远端必须返回该 PINGGOAWAY:用于某一端将要结束连接WINDOW_UPDATE:更新流控窗口大小CONTINUATION:如果 headers 过大,单个 HEADERS 帧难以携带,通过该帧发送额外的 headers接下来,我们重点看下流控相关的帧,主要是 SETTING 与 WINDOW_UPDATE,在连接建立时会通过 SETTINGS 帧来调整对方的窗口大小,之后在传输过程中,窗口大小会随着数据的发送逐渐减小,直到收到对方发送的 WINDOW_UPDATE 帧,从而更新窗口大小。SETTINGS 帧主要包含以下内容:

SETTINGS_HEADER_TABLE_SIZE:HPACK(一种header压缩算法) header 表的最大长度,默认值 4096SETTINGS_ENABLE_PUSH:客户端发向服务端的配置,若设置为 true,客户端将允许服务端推送响应,默认值 trueSETTINGS_MAX_CONCURRENT_STREAMS:同时打开的 stream 最大数量,通常意味着同一时刻能够同时响应的请求数量,默认无限SETTINGS_INITIAL_WINDOW_SIZE:流控的初始窗口大小,默认值 65535SETTINGS_MAX_FRAME_SIZE:对端能够接受帧的最大长度,默认值16384SETTINGS_MAX_HEADER_LIST_SIZE:对端能够接受的 header 列表最大长度,默认不限制流控的实现如上所述,每发送一批 DATA 帧,即将窗口大小减小。需要注意的是流控仅针对 DATA 帧。

前面提到流控既可以作用于 stream 又可以作用于 connection,那具体是怎么执行的呢?connection 的流控与 上述 stream 流控逻辑类似,每次发送 DATA 帧,connection 与 stream 窗口都会减小,但不同的是,WINDOW_UPDATE 要么单独作用于 stream,要么单独作用于 connection(streamid 为 0 时,表示作用于 connection)。

问题定位那么回到开篇的问题,我们以 URL 导致抛异常:

而从这里的代码可以看出,524288 是当前窗口大小,而delta是对方告知的 WINDOW_UPDATE 大小,通过分析,发现 524288 这个值不同于默认值 65535,那继续看这个值是什么时间改动的:

发现是接收 SETTINGS 指令后,初始化窗口大小时修改的,但这里与 RFC 7540 [2]的描述(connection 窗口大小仅在接收到 WINDOW_UPDATE 后才可能修改)是冲突的:

因此我们断定是 ,在删除标记的这行代码后,请求可以正常执行了。

遗憾的是在准备给 中被修复了。

参考资料[1] PTS:

[2] RFC 7540:

/作者:风起

原文链接:

本文为阿里云原创内容,未经允许不得转载。

热门