![](/assets/img/weixin-blog.311ae013.png)
# HTTP1.X的管线化h2的多路复用以及TCP对头阻塞
# HTTP1.1 的管线化和 H2 的多路复用的区别
HTTP/1.1 诞生于 1999 (opens new window) 年,它进一步完善了 HTTP 协议,一直用到了 20 多年后的今天,仍然是使用最广的 HTTP 版本。
核心特点如下:
- 持久连接 (HTTP1.0 的 Keep-Alive 和 HTTP1.1 的 persistent)
HTTP/1.1 默认开启持久连接,在 TCP 连接建立后不立即关闭,让多个 HTTP 请求得以复用。
- 管线化技术
HTTP/1.1 中,多个 HTTP 请求不用排队发送,可以批量发送,这就解决了 HTTP 队头阻塞问题。但批量发送的 HTTP 请求,必须按照发送的顺序返回响应,相当于问题解决了一半,仍然不是最佳体验。
# HTTP1.1 管道化的限制
- 管道化要求服务端按照请求发送的顺序返回响应(FIFO),原因很简单,HTTP 请求和响应并没有序号标识,无法将乱序的响应与请求关联起来。
- 客户端需要保持未收到响应的请求,当连接意外中断时,需要重新发送这部分请求。
- 只有幂等的请求才能进行管道化,也就是只有 GET 和 HEAD 请求才能管道化,否则可能会出现意料之外的结果
# HTTP2.0 多路复用
在 HTTP 2.0 中,有两个非常重要的概念,分别是帧(frame)和流(stream)。
帧代表着最小的数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流。
多路复用,就是在一个 TCP 连接中可以存在多条流。换句话说,也就是可以发送多个请求,对端可以通过帧中的标识知道属于哪个请求。通过这个技术,可以避免 HTTP 旧版本中的队头阻塞问题,极大的提高传输性能。
# 疑问
- 为什么 h2 的多路复用优于 h1.1 的管线化?
h1.1 的管线化本质上传输的方式还是文本,可以并行发起多个请求,并且也能复用同一个 TCP 连接,传输效率得到了提升。但响应端只能按照发送的顺序进行返回,为此很多浏览器会为每个域名至多打开 6 个连接,用增加队列的方式减少 HTTP 队头阻塞。。
h2 利用了 流和帧的概念将数据从文本变成二进制,这样在一个 tcp 连接中就可以同时存在多个流,服务端接收到后按照流的 id 对数据进行重新组装。
# TCP 队头阻塞
参考自TCP 队头阻塞 (opens new window)
HTTP/2 是基于 TCP 实现的。相比之前的版本,HTTP/2 使用的 TCP 连接数少了很多。TCP 是一个可靠的传输协议,基本上,你可以将它视为在两台计算机间建立的一个虚拟链路,由一端放到网络上的内容,最终总会以相同的顺序出现在另一端。(或者遭遇连接中断)
两台计算机间的 TCP 链路
采用 HTTP/2 时,浏览器一般会在单个 TCP 连接中创建并行的几十个乃至上百个传输。
如果 HTTP/2 连接双方的网络中有一个数据包丢失,或者任何一方的网络出现中断,整个 TCP 连接就会暂停,丢失的数据包需要被重新传输。因为 TCP 是一个按序传输的链条,因此如果其中一个点丢失了,链路上之后的内容就都需要等待。
如下图所示,我们一个用链条来表现一个连接上发送的两个流(传输),红色的与绿色的数据流:
不同颜色的链条代表着不同的链路
这种单个数据包造成的阻塞,就是 TCP 上的队头阻塞(head of line blocking)。
随着丢包率的增加,HTTP/2 的表现越来越差。在 2%的丢包率(一个很差的网络质量)中,测试结果表明 HTTP/1 用户的性能更好,因为 HTTP/1 一般有六个 TCP 连接,哪怕其中一个连接阻塞了,其他没有丢包的连接仍然可以继续传输。
在限定的条件下,在 TCP 下解决这个问题相当困难。
# HTTP2.0 的多路复用是否意味着可以毫无节制的发送 http 请求
http2.0 的多路复用和 TCP 的队头阻塞是有区别的。
HTTP2.0 并没有解决 TCP 的头部阻塞:HTTP2.0 在应用层相对于 HTTP1.x 做了二进制分帧和 TCP 连接复用,解决了 HTTP1.x 的队头阻塞问题,但是由于传输层依然是 TCP,所以并没有解决 TCP 的队头阻塞问题。所以并不是请求无限多更优。但相对 HTTP1.x,一定程度的利用 HTTP2.0 的 TCP 连接复用,来高并发发送请求,是有一定优化的。(所以更好的 QUIC 的协议是基于 UDP 协议的)
HTTP2.0 协议内的并发限制参数:在 HTTP2.0 的协议内,有一个叫 SETTINGS_MAX_CONCURRENT_STREAMS 的设置,用于限制对端的最大并发 stream 数量,而这个值在常见的服务端有默认值(即限制浏览器的并发数量),一般是 100 或者 128(可以搜一下 nginx 和 apache 配置,腾讯云 CLB 也是 128)。在抓包工具也能看到。一般不建议调整,因为这个值主要是受服务器的内存/性能限制,也不是越大越好。
特殊情况的局限:由于只有一个 TCP 连接,在高延迟高丢包率下,其性能大大低于 HTTP1.x 的。我记得有个论文,大概是在不同网络状况下的两种协议的传输效率对比,结论是最好的网络下 HTTP2.0 优,最差的网络下 HTTP1.x 优,中间则差不多。
webpack 优化策略:由于无法监测 HTTP2.0 下浏览器的并发数量,我采用的是请求总量控制。webpack 可以通过 maxSize 参数,间接控制文件数量,实际值需要业务方自己调整。
← CDN访问流程及刷新预热 Http缓存详解 →