sourceMap http 2 阻塞问题
问题表现
前段时间公司系统遇到一个稍显神奇的问题,那就是,在打开 devetool 的时候,所有的资源加载会有明显的卡顿情况,例如从 200ms 增加到 1~2s
之后就是一系列的排查过程
- 不启动 devtool 没有问题
- 单独请求资源没有问题
- 切换网络环境从 wifi 切换到有线之后,情况有明显的减缓
- http2 换到 http1.1 情况有好转
到这里还是没有排查出问题。最终基于 wifi 下有问题这一点,又拉上网管去看。最终才发现一个令人尴尬的原因
原因
对,正如标题而言,问题出现的地方在于 sourceMap,我们一步步从结果解释现象
首先,为什么不打开 devtool 就不会有问题?
因为 chrome 只有在打开 devtool 的时候才会去加载 sourceMap,因此这个问题只出现在打开 devtool 的时候 。
加载 sourceMap 怎么触发这个问题?
有一个前提是,出现问题的系统的 sourceMap 非常的大,有 20m+,当我们打开 devtool 的时候:
- 浏览器扫描的 js 中的 sourceMap 就会立即去请求sourceMap
- 这个非常大的 sourceMap 优先级比较高,占据可大多数的带宽
- 此时也在并行加载中的其他资源,因为得不到带宽继而进入阻塞等待状态
因此,最终所有的资源加载就会显得耗时非常的长。
为什么切换网络环境从 wifi 切换到有线之后,情况有明显的减缓?
因为有线网络是千兆网络,带宽比 wifi 大得多,能同时容纳更多的资源请求,因此 sourceMap 不会阻塞其他资源加载。
为什么 http1.1 情况下会好于 http2 问题?
这就是这个问题更加有趣的地方了,我们一般认知中, http2 都是对比 http1.1 有着各种好处。但实际上 http2 在这个问题上却栽了跟头。
原因在于 http2 的多路复用特性
多路复用的初衷是让 http2 所有请求的请求都通过一个 TCP 连接并发完成,避免 http1.1 中需要频繁建立连接带来的性能开销
回到我们的问题上,http2 多路复用下,体积庞大的 sourceMap 和其他相对较小的资源(平均小了两个数量级)共用了一条链路,当并行请求 scourceMap 和其他资源而导致带宽负载满载的时候,丢包率上升,继而出现了队头阻塞问题,
队头阻塞是指当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一并被阻塞,会导致客户端迟迟收不到数据。
而 http2 由于所有资源都在一条链路上,一旦发生阻塞,所有资源的都被阻塞了。
但在 http1.1 的情况下由于没有多路复用,我们的资源请求实际上是分开多个 tcp 连接加载的,这就意味着多条 tcp 连接的速度是相互竞争的,也就是说
假设有 A,B 两条 tcp 连接,A 连接加载了 sourceMap, B 连接加载了其他资源,那么
- A 连接由于一次只能处理一个请求,因此即使是 keepAlie 的情况下 A,连接也必须处理完了当前的sourceMap 再去处理其他资源,而由于 sourcemap 比较大,需要比较长的时间,这个时候会阻塞 A 链路上后续的资源。
- B 连接作为独立的 TCP 链接,并没有受到 A 链接的队头阻塞影响,并且同时在多条 TCP 链接并存的时候,TCP 特性使得每个 TCP 连接可以得到均等的带宽。
因此在 http 1.1 的情况下加载大 sourceMap,依然会阻塞,但是其作用被限制在了单条链路上,而其他资源可以从别的链路上进行加载,继而减轻了整体的加载阻塞情况。
结
这个问题是多个问题共同导致的结果,涉及到了物理带宽问题,http 协议版本不同下加载策略的问题以及项目工程上设置的问题。
尤其是最后 http 协议版本不同带来的问题,更加刷新我的认知
还有一个只得吐槽的点就是 chrome network 中看不到 sourceMap 的加载信息,这也是影响我们排除问题的重大阻碍之一。