Differences between HTTP1.1 and HTTP2
前言
一次面试被问到 http1.1 和 http2 有什么区别,当时随便答了些跟 keep-alive 和 upgrade 相关的内容,从面试官的眼神里体会到了,我的回答应该差的还挺远的,查了下资料总结如下:
背景
http1.0 协议 1987 年发布,后来 www 发展越来越普及,为了提高 http1.0 的传输效率,在 1997 年又公布了 http1.1,主要提出了对一个 tcp 连接的复用,也就是熟知的 connection: keep-alive 特性。然而,为了进一步提高基于 http 协议的应用体验,比如越来越多的移动设备移动娱乐(图片、视频)的需求,google 提出了 spdy 协议,就是为了进一步提高 http 的性能,提高用户体验。http2 协议就是基于 spdy,目前应该还是提案阶段。而且提出 spdy 的同期,google 又提出了 quic 协议,quic 提出了 spdy 中存在的各种问题及部分的解决方案,并提出基于 udp 方式模拟 tcp 的面向连接的方式。
Transmission Model
http1.1 和 http2 的第一个不同在于传输模型的不同。
http1.0 中,对于资源请求是一个 request 一个 response,几乎所有 tcp 都是短链接(除非主动开启 keepalive 特性)。对于一个页面的请求,通常包含了很多资源,如果需要渲染一个包含多资源的页面,只是一应一答的方式,则需要重复建多个 tcp 连接,增加了资源开销。因此 http1.1 中,默认开放了 keep-alive 特性,多个资源的请求可以服用同一个 tcp,降低了建链拆链的开销。这种方式被称为 pipeline。pipeline 的问题是,虽然 tcp 被服用了,但对资源的请求是串行的,如果排在前面的资源请求出现阻塞,则会影响后续的资源传输。这被称为 HOL(head of line) blocking。如果为了解决这个问题,采用并行建多个 tcp 链接的策略,那么无论是客户端还是服务端,都面临更高的开销,尤其是对服务端而言,在并发连接数有上限的情况下则并发客户端服务数量就会大幅度降低。
http2 采用了底层流技术,这个流技术对 http 上层的语义没有影响,只是在数据流的传输上,不再采用 plain text 这种方式。当一次请求中包含多个资源的请求时,将不同的资源映射到不同的二进制流上,每个流有唯一 id,并且通过 parent 字段描述不同的流资源间的相互依赖关系。同时,每个流还可以指定优先级,优先级数字越大则优先应答。对于一个资源的数据,通过流传输时,数据被进一步划分成更小的单位,称为 frame。在一个流通道中(流通道建立在 tcp 协议上),可以同时传输不同 id 的流,实现多个资源的并行,且资源传输的先后顺序可以有应用通过定义优先级的方式灵活定制。
flow control
第二个不同是在 flowcontrol 上的实现方式的改变。
对于客户端和服务端,本身都有一个 buffer,用于缓存不完整的信息,待缓存中的信息完整后,一并处理。考虑如果有大资源需要传输时,可能就会发生 buffer 溢出,因此 tcp 协议中采用了滑动窗口的方式避免 buffer 溢出。接收端在接收数据,发送 ack,在 ack 中可以上报本地 buffer 中尚存的空余空间,发送端可以根据上报的空余空间,调整 发送速率。一旦空余空间为 0,则发送端停止发送。当接收端处理完 buffer 中的数据后,再请求发送端继续发送。http1.1 的流控即依赖了 tcp 的滑动窗口实现。
由于 http2.0 提出了并行流的传输方式,它的 flow control 也是按流来约定的。这个约定被放到了应用层,即应用可以基于自身的 buffer 请求动态协商缓存大小。同时,在端到端传输过程中流经的中间节点也可以识别 flow control 参数,从而实现更好的流控。
Predicting Resource Requests
第三个不同是资源预请求策略的不同。
在一次页面的 http 请求中,为了渲染一个页面,除了需要请求 html 页面文件外,还需要请求 css。http1.1 中,如果预测到某些资源在后续的请求中必定会发送,就可以在第一的请求中将这部分资源一并应答给客户端,这种称为 resource inlining。但这样的方式有一定问题,比如可能会重复应答客户端已有的资源,当被 inline 的资源非常大的情况下,对网络资源的浪费也非常大,所以在实际情况下,很少有应用这种方式的。
http2 提出了叫 server push 的方式。如果预测到某些资源是可能会被后续请求的,则先向客户端推送一条 PUSH_PROMISE 帧,在这条帧中描述了即将推送过来的内容的元数据。如果客户端不需要某些资源,则可以应答一条 RST_STREAM 来取消某些资源。这样就避免了资源浪费。同时,客户端还可以发送 SETTINGS
帧来改变 server push 的行为。
Compression
最后一个不同是压缩。http1.1 只对消息体进行压缩,不对 http header 压缩,因为 header 一般很小。但是当请求量比较大时,header 对网络带宽的开销也会增大。http2 定义 hpack 的方式对 header 也进行了压缩,尤其是当两次请求或应答时头部仅有部分差异时,只传输差异部分。
Conclusion
以上是几个主要的不同点,如 multiplexing, stream priorization, flow control, server push and compression。如果要了解 http1.1 和 http2 性能上的差异分析,可以参考 HTTP/2 – A Real-World Performance Test and Analysis,同时可以关注 ietf 关于 http2 协议的文档。