• Http2协议简介


    1.概述

    • 和http1兼容。HTTP/2 没有改动 HTTP 的应用语义。 HTTP 方法、状态代码、URI 和标头字段等核心概念一如往常。 不过,HTTP/2 修改了数据格式化(分帧)以及在客户端与服务器间传输的方式。因此,所有现有的应用都可以不必修改而在新协议下运行。
    • 传输方式改变。在HTTP/1.x中,每个 TCP 连接同时只能处理一个请求 - 响应,一个请求就会独占一个链接,用户想要多个并行的请求来提高性能,必须得使用多个TCP连接(对于同一个域名Chrome最多只能同时创建 6 个 TCP 连接)。这也是被人吐槽的原因,也是http2 要解决的一个痛点,解决方法是在链接的基础上提出了stream的概念,通过stream 来区别不同的请求 。

    2.连接多路复用

    在一个 TCP 连接上,我们可以向对方不断发送帧,每帧的 stream identifier 的标明这一帧属于哪个流,然后在对方接收时,根据 stream identifier 拼接每个流的所有帧组成一整块数据。

    把 HTTP/1.1 每个请求都当作一个流,那么多个请求变成多个流,请求响应数据分成多个帧,不同流中的帧交错地发送给对方,这就是 HTTP/2 中的多路复用。

    流的概念实现了单连接上多请求 - 响应并行,解决了线头阻塞的问题。

    (1) 帧

    +---+-----------+------------+-------------+-------------+
    |Bit|   0...7   |   8...15   |   16...23   |   24...31   |
    +---+-----------+------------+-------------+-------------+
    |0  |              Length                  |     Type    |
    +---+-----------+--------------------------+-------------+
    |32 |Flags      |
    +---+-+---------+----------------------------------------+
    |40 |R|                Stream Identifier                 |
    +---+-+--------------------------------------------------+
    |...|                   Frame Payload                    |
    +---+----------------------------------------------------+

    字段含义:

    • Length 代表整个 frame 的长度,用一个 24 位无符号整数表示。
    • Type 定义 frame 的类型,用 8 bits 表示。帧类型决定了帧主体的格式和语义,如果 type 为 unknown 应该忽略或抛弃。
    • Flags 是为帧类型相关而预留的布尔标识。标识对于不同的帧类型赋予了不同的语义。如果该标识对于某种帧类型没有定义语义,则它必须被忽略且发送的时候应该赋值为 (0x0) 。
    • R 是一个保留的比特位。这个比特的语义没有定义,发送时它必须被设置为 (0x0), 接收时需要忽略。
    • Stream Identifier 用作流控制,用 31 位无符号整数表示。客户端建立的 sid 必须为奇数,服务端建立的 sid 必须为偶数,值 (0x0) 保留给与整个连接相关联的帧 (连接控制消息),而不是单个流 。
    • Frame Payload 是主体内容,由帧类型决定。

    共分为十种类型的帧:

    • HEADERS: 报头帧 (type=0x1),用来打开一个流或者携带一个首部块片段
    • DATA: 数据帧 (type=0x0),装填主体信息,可以用一个或多个 DATA 帧来返回一个请求的响应主体
    • PRIORITY: 优先级帧 (type=0x2),指定发送者建议的流优先级,可以在任何流状态下发送 PRIORITY 帧,包括空闲 (idle) 和关闭 (closed) 的流
    • RST_STREAM: 流终止帧 (type=0x3),用来请求取消一个流,或者表示发生了一个错误,payload 带有一个 32 位无符号整数的错误码 (Error Codes),不能在处于空闲 (idle) 状态的流上发送 RST_STREAM 帧
    • SETTINGS: 设置帧 (type=0x4),设置此 连接 的参数,作用于整个连接
    • PUSH_PROMISE: 推送帧 (type=0x5),服务端推送,客户端可以返回一个 RST_STREAM 帧来选择拒绝推送的流
    • PING: PING 帧 (type=0x6),判断一个空闲的连接是否仍然可用,也可以测量最小往返时间 (RTT)
    • GOAWAY: GOWAY 帧 (type=0x7),用于发起关闭连接的请求,或者警示严重错误。GOAWAY 会停止接收新流,并且关闭连接前会处理完先前建立的流
    • WINDOW_UPDATE: 窗口更新帧 (type=0x8),用于执行流量控制功能,可以作用在单独某个流上 (指定具体 Stream Identifier) 也可以作用整个连接 (Stream Identifier 为 0x0),只有 DATA 帧受流量控制影响。初始化流量窗口后,发送多少负载,流量窗口就减少多少,如果流量窗口不足就无法发送,WINDOW_UPDATE 帧可以增加流量窗口大小
    • CONTINUATION: 延续帧 (type=0x9),用于继续传送首部块片段序列,见 首部的压缩与解压缩

    (2) 消息

    一个HTTP2请求或响应被分为N个帧传送,在另一端再重新组合为完整的消息。

    (3) 流

    流是一个逻辑上的概念,代表 HTTP/2 连接中在客户端和服务器之间交换的独立双向帧序列,每个帧的 Stream Identifier 字段指明了它属于哪个流。 流有以下特性:

    • 单个连接可以包含多个流,两端之间可以交叉发送不同流的帧
    • 流可以由客户端或服务器来单方面地建立和使用,或者共享
    • 流可以由任一方关闭
    • 帧在流上发送的顺序很重要,最后接收方会把相同 Stream Identifier (同一个流) 的帧重新组装成完整的消息

    3.HTTP头压缩

    简单说,HTTP头压缩需要在HTTP/2 客户端和服务端之间:

    • 维护一份相同的静态表(Static Table),包含常见的头部名称,以及特别常见的头部名称与值的组合;
    • 维护一份相同的动态表(Dynamic Table),可以动态地添加内容;
    • 基于静态哈夫曼码表的哈夫曼编码(Huffman Coding);

    (1) 静态索引

    静态索引表是固定的,对于客户端服务端都一样,目前协议商定的静态索引包含 61 个键值,详见 Static Table Definition - RFC 7541

    前几个如下

    索引字段值键值
    1 :authority  
    2 :method GET
    3 :method POST
    4 :path /
    5 :path /index.html
    6 :scheme http
    7 :scheme https
    8 :status 200

    (2) 动态索引

    动态索引表是一个 FIFO 队列维护的有空间限制的表,里面含有非静态表的索引。
    动态索引表是需要连接双方维护的,其内容基于连接上下文,一个 HTTP2 连接有且仅有一份动态表。
    当一个首部匹配不到索引时,可以选择把它插入动态索引表中,下次同名的值就可能会在表中查到索引并替换。
    但是并非所有首部键值都会存入动态索引,因为动态索引表是有空间限制的,最大值由 SETTING 帧中的 SETTINGS_HEADER_TABLE_SIZE (默认 4096 字节) 设置

    4.HTTP/2 的协议协商机制

    客户端在不知道服务器是否支持HTTP2协议时需要使用HTTP Upgrade 机制。

    客户端首先发起一个 HTTP/1.1 请求,其中包含Upgrade首部字段,还必须包含一个且只能一个 HTTP2-Settings 首部字段。

    例如:

    GET / HTTP/1.1
    Host: server.example.com
    Connection: Upgrade, HTTP2-Settings
    Upgrade: h2c
    HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>
    • Upgrade。"h2c" 标示运行在明文 TCP 之上的 HTTP/2 协议。"h2" 标示使用了 TLS(Transport Layer Security)[TLS12] 的 HTTP/2 协议
    • HTTP2-Settings。是一个连接相关的首部字段,它提供了用于管理 HTTP/2 连接的参数(前提是服务端接受了升级请求)

    不支持 HTTP/2 的服务端响应请求时,可以认为 Upgrade 首部字段不存在。

    支持 HTTP/2 的服务器响应状态码 101 (Switching Protocols) 表示接受升级协议的请求。在结束 101 响应之后,服务端可以开始发送 HTTP/2 帧。

    例如:

    HTTP/1.1 101 Switching Protocols
    Connection: Upgrade
    Upgrade: h2c
    
    [ HTTP/2 connection ...

    客户端一收到 101(Switching Protocols) 响应(表示成功升级)后,就发送客户端连接前奏。客户端连接前奏以一个固定的24字节的序列开始,用十六进制表示为:0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a(相当于一个魔法值,在WireSark可以看到标识为Magic)。

    为了避免不必要的延迟,允许客户端发送完连接前奏后就立即向服务端发送其他的帧,而不必等待服务端的连接前奏。

    服务端发送的第一个 HTTP/2 帧必须是由一个 SETTINGS 帧组成的服务端连接前奏。

    两端都要发送一个连接前奏,作为对所使用协议的最终确认,并确定 HTTP/2 连接的初始设置。

    5.流量控制

    多路复用的流会竞争 TCP 资源,进而导致流被阻塞。流控制机制确保同一连接上的流不会相互干扰。流量控制作用于单个流或整个连接。HTTP/2 通过使用 WINDOW_UPDATE 帧来提供流量控制。 流控制具有以下特征:

    • 流量控制是特定于连接的。两种级别的流量控制都位于单跳的端点之间,而不是整个端到端的路径。比如 server 前面有一个 front-end proxy 如 Nginx,这时就会有两个 connection,browser-Nginx, Nginx—server,flow control 分别作用于两个 connection。详情见: How is HTTP/2 hop-by-hop flow control accomplished? - stackoverflow
    • 流量控制是基于 WINDOW_UPDATE 帧的。接收方公布自己打算在每个流以及整个连接上分别接收多少字节。这是一个以信用为基础的方案。
    • 流量控制是有方向的,由接收者全面控制。接收方可以为每个流和整个连接设置任意的窗口大小。发送方必须尊重接收方设置的流量控制限制。客户方、服务端和中间代理作为接收方时都独立地公布各自的流量控制窗口,作为发送方时都遵守对端的流量控制设置。
    • 无论是新流还是整个连接,流量控制窗口的初始值是 65535 字节。
    • 帧的类型决定了流量控制是否适用于帧。目前,只有 DATA 帧会受流量控制影响,所有其它类型的帧并不消耗流量控制窗口的空间。这保证了重要的控制帧不会被流量控制阻塞。
    • 流量控制不能被禁用。
    • HTTP/2 只定义了 WINDOW_UPDATE 帧的格式和语义,并没有规定接收方如何决定何时发送帧、发送什么样的值,也没有规定发送方如何选择发送包。具体实现可以选择任何满足需求的算法。

    (1) WINDOW_UPDATE 帧格式

    +-+-------------------------------------------------------------+
    |R|                Window Size Increment (31)                   |
    +-+-------------------------------------------------------------+
    • Window Size Increment 表示除了现有的流量控制窗口之外,发送端还可以传送的字节数。取值范围是 1 到 2^31 - 1 字节。

    WINDOW_UPDATE 帧可以是针对一个流或者是针对整个连接的。如果是前者,WINDOW_UPDATE 帧的流标识符指明了受影响的流;如果是后者,流标识符为 0 表示作用于整个连接。
    流量控制功能只适用于被标识的、受流量控制影响的帧。文档定义的帧类型中,只有 DATA 帧受流量控制影响。除非接收端不能再分配资源去处理这些帧,否则不受流量控制影响的帧必须被接收并处理。如果接收端不能再接收帧了,可以响应一个 FLOW_CONTROL_ERROR 类型的流错误或者连接错误。

    (2) 流量控制窗口

    流量控制窗口是一个简单的整数值,指出了准许发送端传送的数据的字节数。窗口值衡量了接收端的缓存能力。

    新建连接时,流和连接的初始窗口大小都是 2^16 - 1(65535) 字节。可以通过设置连接前言中 SETTINGS 帧的 SETTINGS_INITIAL_WINDOW_SIZE 参数改变流的初始窗口大小,这会作用于所有流。而连接的初始窗口大小不能改,但可以用 WINDOW_UPDATE 帧来改变流量控制窗口,这是为什么连接前言往往带有一个WINDOW_UPDATE 帧的原因。

  • 相关阅读:
    hdu 1863
    数据结构与算法分析–Minimum Spanning Tree(最小生成树)
    hdu 1856 More is better
    hdu 1272 小希的迷宫
    数据结构与算法分析 – Disjoint Set(并查集)
    数字逻辑电路课程设计报告
    高校成绩管理数据库系统的设计与实现
    PL/0编译器(java version) – SymbolTable.java
    [jquery]添加行内容后根据下拉菜单选择内容对比之前已有选项,若有重置再提示
    bootstrap-datetimepicker 日期控件的开始日期
  • 原文地址:https://www.cnblogs.com/Alpharun/p/12060049.html
Copyright © 2020-2023  润新知