Http2.0 协议

目前国内大部分互联网都还在使用 HTTP1.1 协议对外提供 API 服务,不过很多大型互联网公司都已经有大量的业务线在使用了 HTTP2.0 协议,因为 Http2.0 支持的 Streaming 和 Duplexing 可以将请求和响应消息进行分片交叉传送,可以大幅提升传输效率。据 W3Techs 统计,截止 2018 年 6 月份,已经有 26% 的大型互联网服务已经开始使用 HTTP2.0 协议。

讲协议不是目的,重要的从中学习到设计技巧,为下一章的重头戏gRPC做铺垫。

HTTP 1.x

这里列举我们一些这个版本中习以为常的痛点

HTTP header

要解析一个完整的 HTTP 请求,首先我们需要能正确的读出 HTTP header。而HTTP header 属于消息的文本模式,它各个 fields 使用 \r\n 分隔。解析完 header 之后,我们才能从 header 里面的 content-length 拿到 body 的 size,从而读取 body。

这套流程其实并不高效,因为我们需要读取多次,才能将一个完整的 HTTP 请求给解析出来。也有一个非常高效小巧的 C library:http-parser,有不少框架都是集成了它

Request/Response

HTTP1.1 还是基于文本协议的问答有序模式,也就是client 发送了 request 之后,必须等到 response,才能继续发送下一次请求。这套机制是非常简单,但会造成网络连接利用率不高。所以为了性能,通常这些连接都是长连接(HTTP KeepAlive)一直保活的,虽然对于 server 来说同时处理百万连接也没啥太大的挑战,但终归效率不高。

Push

做过推送的同学,大概就知道有多么的痛苦,因为 HTTP/1.x 并没有推送机制。Long polling 方式,也就是直接给 server 挂一个连接,等待一段时间(譬如 1 分钟),如果 server 有返回或者超时,则再次重新 poll。

HTTP 2.0

HTTP/2 是一个二进制协议,这也就意味着它的可读性几乎为 0,但幸运的是,我们还是有很多工具,譬如 Wireshark, 能够将其解析出来。

Stream一个双向流,一条连接可以有多个 streams
Message 也就是逻辑上面的 request,response
Frame数据传输的最小单位。每个 Frame 都属于一个特定的 stream 或者整个连接。一个 message 可能有多个 frame 组成。

duplexing

HTTP/2 通过 stream 支持了连接的多路复用,提高了连接的利用率。Stream 有很多重要特性:

  • 一条连接可以包含多个 streams,多个 streams 发送的数据互相不影响
  • Stream 可以被 client 和 server 单方面或者共享使用
  • Stream 可以被任意一段关闭
  • Stream 会确定好发送 frame 的顺序,另一端会按照接受到的顺序来处理
  • Stream 用一个唯一 ID 来标识

HTTP2.0 是基于二进制协议的乱序模式 (Duplexing)。这意味同一个连接通道上多个请求并行时,服务器处理快的可以先返回而不用因为等待其它请求的响应而排队。

为了更大的提高一条连接上面的 stream 并发,可以考虑调大 SETTINGS_MAX_CONCURRENT_STREAMS,如果这个值比较小,整体吞吐上不去的问题。

注意:虽然一条连接上面能够处理更多的请求了,但一条连接远远是不够的。一条连接通常只有一个线程来处理,所以并不能充分利用服务器多核的优势。

header 优化:HPACK

HTTP 协议的请求头有大量的 key/value 文本组成,多个请求直接 key/value 重复程度很高。为了优化这部分,HTTP2.0采用了 HPACK,虽然 HPACK 的 RFC 文档 看起来比较恐怖,但其实原理非常的简单易懂。

HPACK 提供了一个静态和动态的 table,静态 table 定义了通用的 HTTP header fields,对于常用的 key/value 文本无需重复传送,而是通过引用内部字典的整数索引来达到显著节省请求头传输流量的目的。对于动态 table,初始化为空,如果两边交互之后,发现有新的 field,就添加到动态 table 上面,这样后面的请求就可以跟静态 table 一样,只需要带上相关的 index 就可以了。

framing & streaming

在 HTTP1.1 里面如果一个响应的内容太大,一般使用 chunk 模式进行分块传输。在 HTTP2.0 里面它也有分块传送,但是概念上叫着Framing——分帧传送。HTTP2.0 的消息都是分帧传送的,它是最小的数据传输单位,所有的消息都是一帧一帧的,有些小请求 / 响应只有一个帧,还有些大请求 / 响应因为内容多需要多个帧。每个帧的格式如上图所示,包含长度前缀、消息类型、消息标志位和消息内容 5 个部分。

同一个响应会有同一个流 ID stream_id,消息接收端会将具有相同stream_id的消息帧串起来作为一个整体来处理,位于流的最后一个消息帧会有一个流结束标志位(EOS——End of Stream)

注: EOS 并不意味着连接的中止,同一个连接上会有多个流穿插传输,结束了单个流还会有很多其它的流在继续传送。

一个 Frame 定义如下(直接从官网 copy 的

+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+-------------------------------+
|R| Stream Identifier (31) |
+=+=============================================================+
| Frame Payload (0...) ...
+---------------------------------------------------------------+

Length :也就是 Frame 的长度,默认最大长度是 16KB,如果要发送更大的 Frame,需要显示的设置 max frame size

R:保留位,可以先不管

Stream Identifier:标识所属的 stream的stream_id如果为 0,则表示这个 frame 属于整条连接。如果是 client 创建的 stream,ID 就是奇数,如果是 server 创建的,ID 就是偶数。ID 0x00 和 0x01 都有特定的使用场景,不会用到。Stream ID 不可能被重复使用,如果一条连接上面 ID 分配完了,client 会新建一条连接。

Frame Payload:根据不同 Type 有不同的格式

Type :HTTP2.0 默认定义了十种类型的帧,因为 type 字段是一个字节,可以支持 255 个类型,所以帧类型是可以扩展的。

  1. HEADERS 帧 ,头信息对应于HTTP HEADER
  2. DATA 帧 ,对应于HTTP Response Body
  3. PRIORITY 帧,用于调整流的优先级
  4. RST_STREAM 帧, 流终止帧,用于中断资源的传输
  5. SETTINGS 帧, 用户客户服务器交流连接配置信息
  6. PUSH_PROMISE 帧, 服务器向客户端主动推送资源
  7. GOAWAY 帧, 礼貌地通知对方断开连接
  8. PING 帧, 心跳帧,检测往返时间和连接可用性
  9. WINDOW_UPDATE 帧, 调整帧窗口大小
  10. CONTINUATION 帧, HEADERS 帧太大时分块的续帧

Flags :HTTP2.0 定义了三种常用的标志位,它可以理解为帧的属性。

  1. END_STREAM, 流结束标志,表示当前帧是流的最后一个帧
  2. END_HEADERS ,头结束标志,表示当前帧是头信息的最后一个帧
  3. PADDED,填充标志,在数据 Payload 里填充无用信息,用于干扰信道监听

通常我们最常用的 HTTP 请求 / 响应的帧形式如下

Priority

因为一条连接允许多个 streams 在上面发送 frame,那么在一些场景下面,我们还是希望 stream 有优先级,方便对端为不同的请求分配不同的资源。譬如对于一个 Web 站点来说,优先加载重要的资源,而对于一些不那么重要的图片啥的,则使用低的优先级。

我们还可以设置 Stream Dependencies,形成一棵 streams priority tree。假设 Stream A 是 parent,Stream B 和 C 都是它的孩子,B 的 weight 是 4,C 的 weight 是 12,假设现在 A 能分配到所有的资源,那么后面 B 能分配到的资源只有 C 的 1/3。

Flow Control

HTTP/2 也支持流控,如果 sender 端发送数据太快,receiver 端可能因为太忙,或者压力太大,或者只想给特定的 stream 分配资源,receiver 端就可能不想处理这些数据。譬如,如果 client 给 server 请求了一个视屏,但这时候用户暂停观看了,client 就可能告诉 server 别在发送数据了。

虽然 TCP 也有 flow control,但它仅仅只对一个连接有效果。HTTP/2 在一条连接上面会有多个 streams,有时候,我们仅仅只想对一些 stream 进行控制,所以 HTTP/2 单独提供了流控机制。Flow control 有如下特性:

  • Flow control 是单向的。Receiver 可以选择给 stream 或者整个连接设置 window size。
  • Flow control 是基于信任的。Receiver 只是会给 sender 建议它的初始连接和 stream 的 flow control window size。
  • Flow control 不可能被禁止掉。当 HTTP/2 连接建立起来之后,client 和 server 会交换 SETTINGS frames,用来设置 flow control window size。
  • Flow control 是 hop-by-hop,并不是 end-to-end 的,也就是我们可以用一个中间人来进行 flow control。

HTTP/2 默认的 window size 是 64 KB,实际这个值太小了,场景需要可以设置成 1 GB。

引用资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值