HTTP2 的流控实现

本文涉及的产品
性能测试 PTS,5000VUM额度
简介: 开篇吹一波阿里云性能测试服务 [PTS](https://help.aliyun.com/document_detail/145501.html),PTS 在 2021年 5月份已经上线了对 HTTP2 协议的支持(底层依赖 httpclient5),在压测时会通过与服务端协商的结果来决定使用 HTTP1.1 或者 HTTP2 协议。### 背景写这篇文章的原因是某天某个客户找过来,问我

开篇吹一波阿里云性能测试服务 PTS,PTS 在 2021年 5月份已经上线了对 HTTP2 协议的支持(底层依赖 httpclient5),在压测时会通过与服务端协商的结果来决定使用 HTTP1.1 或者 HTTP2 协议。

背景

写这篇文章的原因是某天某个客户找过来,问我们是不是不支持 HTTP2,因为他在腾讯云上购买了2个域名,其中一个开启了 HTTP2,而在 PTS 压测过程中,支持 HTTP2 的接口总是报错:

起初怀疑是 HTTP2 支持的问题,通过在本地强制使用 HTTP2 协议,访问淘宝主页,发现是没问题的,怀疑是用户在腾讯云上的配置问题,但紧接着通过在本地 Postman、curl 以及压测引擎强制使用 HTTP1.1 协议时都能够正常访问该网页,意识到大概率是 PTS 引擎侧的问题。

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

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

HTTP2 流控

提到窗口,就要提到 HTTP2 相比于 HTTP1.1 支持的新特性:流控(Flow Control),其实 HTTP1.1 依赖于传输层 TCP 的滑动窗口一样可以实现流控,那么为什么 HTTP2 要在应用层再实现一个流控呢?原因在于 HTTP2 引入了流和多路复用,通过流控可以达到使多个流协同的效果。

一些流控的基本概念:

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

为了便于理解,先简单列一下 HTTP2 帧的类型:

  • DATA:携带请求或响应中的数据
  • HEADERS:用于新建一个流(请求或响应),包含对应的 Headers
  • PRIORITY:用于配置流的优先级
  • RST_STREAM:强制结束某个流,仅用于某一端取消流,并不适用于正常流的结束
  • SETTINGS:H2 建联的一些配置
  • PUSH_PROMISE:服务端推送响应到客户端
  • PING:向远端发送一条 PING,远端必须返回该 PING
  • GOAWAY:用于某一端将要结束连接
  • WINDOW_UPDATE:更新流控窗口大小
  • CONTINUATION:如果 headers 过大,单个 HEADERS 帧难以携带,通过该帧发送额外的 headers

接下来,我们重点看下流控相关的帧,主要是 SETTING 与 WINDOW_UPDATE,在连接建立时会通过 SETTINGS 帧来调整对方的窗口大小,之后在传输过程中,窗口大小会随着数据的发送逐渐减小,直到收到对方发送的 WINDOW_UPDATE 帧,从而更新窗口大小。SETTINGS 帧主要包含以下内容:

  • SETTINGS_HEADER_TABLE_SIZE:HPACK(一种header压缩算法) header表的最大长度,默认值 4096
  • SETTINGS_ENABLE_PUSH:客户端发向服务端的配置,若设置为 true,客户端将允许服务端推送响应,默认值 true
  • SETTINGS_MAX_CONCURRENT_STREAMS:同时打开的 stream 最大数量,通常意味着同一时刻能够同时响应的请求数量,默认无限
  • SETTINGS_INITIAL_WINDOW_SIZE:流控的初始窗口大小,默认值 65535
  • SETTINGS_MAX_FRAME_SIZE:对端能够接受帧的最大长度,默认值16384
  • SETTINGS_MAX_HEADER_LIST_SIZE:对端能够接受的 header 列表最大长度,默认不限制

流控的实现如上所述,每发送一批 DATA 帧,即将窗口大小减小。需要注意的是流控仅针对 DATA 帧。

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

问题定位

那么回到开篇的问题,我们以 URL https://www.sysgeek.cn/ 为例,通过在本地做代码 debug 发现,最终抛异常的原因在于接收到 WINDOW_UPDATE 帧后,更新后窗口大小值大于 2^32 - 1导致抛异常:

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

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

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

遗憾的是在准备给 httpcore5 提 PR 的过程中发现这个 bug 已经在 commit 中被修复了。

参考资料

https://datatracker.ietf.org/doc/html/rfc7540#section-5.2
https://undertow.io/blog/2015/04/27/An-in-depth-overview-of-HTTP2.html
https://laike9m.com/blog/rfc7540-bi-ji-wu-flow-control,106/

相关实践学习
通过性能测试PTS对云服务器ECS进行规格选择与性能压测
本文为您介绍如何利用性能测试PTS对云服务器ECS进行规格选择与性能压测。
目录
相关文章
|
11月前
|
数据采集 缓存 算法
最常用的限流算法以及如何在http中间件中加入流控
最常用的限流算法以及如何在http中间件中加入流控
|
数据采集 存储 缓存
最常用的限流算法以及如何在http中间件中加入流控 | 周末学习
通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理
179 0
|
存储 Web App开发 监控
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
我们以前使用过的对hbase和hdfs进行健康检查,及剩余hdfs容量告警,简单易用 1.针对hadoop2的脚本: #/bin/bashbin=`dirname $0`bin=`cd $bin;pwd`STATE_OK=...
1046 0
|
Web App开发 前端开发
|
Web App开发 前端开发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
Found lingering reference异常 ERROR: Found lingering reference file hdfs://jiujiang1:9000/hbase/month_hotstatic/...
713 0
|
Web App开发 前端开发 Java
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
java链接MongoDB处理大量数据时经常碰到cursor not found 的异常,其实是超时所致 Exception in thread "main" com.
826 0
|
Web App开发 监控 前端开发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
负载均衡: LVS(Layer 4), HAProxy(Layer 4、 7),Nginx(Layer 7) 虚拟化: LXC、KVM、Xen HA:Keepalived、Heartbeat 分布式缓存...
754 0
|
Web App开发 监控 前端开发
|
Web App开发 前端开发 Java
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
 Connection reset by peer的常见原因: 1)服务器的并发连接数超过了其承载量,服务器会将其中一些连接关闭;    如果知道实际连接服务器的并发客户数没有超过服务器的承载量,看下有没有网络流量异常。
853 0
|
Web App开发 存储 前端开发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
NoSuchObjectException(message:There is no database named cloudera_manager_metastore_canary_test_db_hive_hivemetastore_df61080e04cd7eb36c4336f71b5a8bc4) at org.
1075 0

热门文章

最新文章