HTTP/2 [RFC 7540] (一)—— 概念介绍

in 开发 with 0 comment

HTTP/2 作为 SPDY 的后继者,目的是为了减少 Web 服务的延迟,完善 HTTP/1.1 提供的功能,而后发展成型的一套应用层协议。目前,大部分现代浏览器都已经支持 HTTP/2 的功能。

本文将对 HTTP/2 与 HTTP/1.1 进行比较,让我们能够对 HTTP/2 有一个基本的了解。

有什么亮点

HTTP/2 是为了解决 HTTP/1.1 高延迟,连接无法多路复用等问题而生的。

HTTP/2 完全兼容 HTTP/1.1 的特性; HTTP/2 提供了服务端 Push 给客户端的能力,在以往只有用 Long Polling 或者 Websocket 做到。

以前我们对 HTTP 协议做压缩的时候,我们都只能对 HTTP Body 部分进行压缩,而 HTTP/2 中,提供了对报文头部的压缩,使得我们能减少传输的字节数。

HTTP/2 在传输层引入了 Stream 的概念,使得多个报文的传输不再依次等待,多路复用连接的方式充分利用了带宽。

帧(Frame) 的定义

我们来讲讲他们之间报文的不同,HTTP/2 的报文比 HTTP/1.1 的报文复杂许多,不像 HTTP/1.1 只有 Header 和 Body 来定义行为。HTTP/2 首先引入了帧的概念,每一帧都是一个数据包,不同的数据包用一个包类型来区分,不同的数据包就是不同的指令,有以下几种类型:

  1. DATA
  2. HEADERS
  3. PRIORITY
  4. RST_STREAM
  5. SETTINGS
  6. PUSH_PROMISE
  7. PING
  8. GOAWAY
  9. WINDOW_UPDATE
  10. CONTINUATION

具体可以看 http://httpwg.org/specs/rfc7540.html 里的 Section 6。

HTTP/2 对数据可以以分包的形式传输,借助CONTINUATION指令,可以继续传输未传输完的内容。

这么多指令里,DATAHEADERS 其实已经可以覆盖 HTTP/1.1 里的内容。剩下的指令除了实现Server Push的功能,还为了针对单连接多 Stream 的优化,做了一些流量控制 (Flow Control) 的命令,如:WINDOW_UPDATE,以解决不同 Stream 之间互相干扰的问题。

帧结构

HTTP/2 的帧结构如下:

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

各字段的长度在上面已有标识,那么分别解释下这些字段的意思:

Stream Identifier

流标识符(后面成为 Stream ID),用来标识这一次帧的交换,如果是客户端发起的 Stream,那么流标识符必然是一个奇数,如果是服务端发起的 Stream (PUSH),那么流标识符一定是一个偶数。0 为标识的 Stream ID 已经被用做连接控制的消息,因此实现方不可作业务使用。如果是从 HTTP/1.1 升级成为 HTTP/2 的时候,那么升级之后的服务端帧响应就作为对标识符1的应答,然后这个 Stream 就进入 half-closed (local) 状态,因此,一个从 HTTP/1.1 升级成为 HTTP/2 的客户端不能选择 1 作为 Stream ID。

一个新建的 Stream ID 必须高于所有已知的 Stream ID。

所有的 Stream 都在一个状态机中进行状态转换,Stream ID 不可重用。如果一个长连接把 Stream ID 耗尽,不能生成新的 Stream ID 的时候,如果是客户端,这时候可以进行重连;服务端则发送一个 GOAWAY 帧报告给客户端,强制客户端重新连接服务端。

Stream States

                             +--------+
                     send PP |        | recv PP
                    ,--------|  idle  |--------.
                   /         |        |         \
                  v          +--------+          v
           +----------+          |           +----------+
           |          |          | send H /  |          |
    ,------| reserved |          | recv H    | reserved |------.
    |      | (local)  |          |           | (remote) |      |
    |      +----------+          v           +----------+      |
    |          |             +--------+             |          |
    |          |     recv ES |        | send ES     |          |
    |   send H |     ,-------|  open  |-------.     | recv H   |
    |          |    /        |        |        \    |          |
    |          v   v         +--------+         v   v          |
    |      +----------+          |           +----------+      |
    |      |   half   |          |           |   half   |      |
    |      |  closed  |          | send R /  |  closed  |      |
    |      | (remote) |          | recv R    | (local)  |      |
    |      +----------+          |           +----------+      |
    |           |                |                 |           |
    |           | send ES /      |       recv ES / |           |
    |           | send R /       v        send R / |           |
    |           | recv R     +--------+   recv R   |           |
    | send R /  `----------->|        |<-----------'  send R / |
    | recv R                 | closed |               recv R   |
    `----------------------->|        |<----------------------'
                             +--------+

       send:   endpoint sends this frame
       recv:   endpoint receives this frame

       H:  HEADERS frame (with implied CONTINUATIONs)
       PP: PUSH_PROMISE frame (with implied CONTINUATIONs)
       ES: END_STREAM flag
       R:  RST_STREAM frame

Stream 的生命周期如上图所示,一个 Stream 在不同阶段有不同的状态:

那么这篇文章已经把帧这个概念介绍完了,下一次我们看看完整的 HTTP 信息交换是如何完成的,以及还有 Server Push 是如何实现的。

非常欢迎一起探讨!

关注TalkWithMobile,关注我