优化相关,合并HTTP请求
这里的合并主要针对当前页面上访问的资源文件,比如css,js,图片等。
HTTP请求过程
一个HTTP请求的主要过程是:
DNS解析(T1) -> 建立TCP连接(T2) -> 发送请求(T3) -> 等待服务器返回首字节(TTFB)(T4) -> 接收数据(T5)。
如下图所示,是Chrome Devtools中显示的一个HTTP请求,显示了HTTP请求的主要阶段,注意,Queueing阶段是请求在浏览器队列中的排队时间,并不计入HTTP请求时间。
从这个过程中,可以看出如果合并N个HTTP请求为1个,可以节省(N-1)* (T1+T2+T3+T4) 的时间。
实验论证
我们来做4组实验,对比一个HTTP请求加载合并后的资源所需时间,和多个HTTP请求并行加载拆分的资源所需时间。每组实验所用资源的体积大小有显著差异。
实验环境:
服务器:阿里云ECS 1核 2GB内存 带宽1M
Web服务器:Nginx (未启用Gzip)
Chrome v66 隐身模式,禁用缓存
Client 网络:wifi 带宽20M
实验 1
// parallel-large.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Parallel Large</title> <link rel="stylesheet" type="text/css" media="screen" href="large1.css" /> <link rel="stylesheet" type="text/css" media="screen" href="large2.css" /> <link rel="stylesheet" type="text/css" media="screen" href="large3.css" /> <link rel="stylesheet" type="text/css" media="screen" href="large4.css" /> <link rel="stylesheet" type="text/css" media="screen" href="large5.css" /> <link rel="stylesheet" type="text/css" media="screen" href="large6.css" /> </head> <body> Hello, world! </body> </html>
// combined-large.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Combined Large</title> <link rel="stylesheet" type="text/css" media="screen" href="large-6in1.css" /> </head> <body> Hello, world! </body> </html>
分别刷新2个页面各10次,利用Devtools 的Network计算CSS资源加载的平均时间。
注意事项:
- large1.css、large2.css … large6.css的加载时间,计算方式为从第一个资源的HTTP请求发送开始,到6个文件都下载完成的时间,如图2红色框内的时间。
- 两个html页面不能同时加载,否则带宽为两个页面所共享,会影响测试结果。需要等待一个页面加载完毕后,再手动刷新加载另外一个页面。
- 页面两次刷新时间间隔在1分钟以上 ,以避免HTTP 1.1 连接复用对实验的影响。
实验结果如下:
large-6in1.css | large1.css、large2.css … large6.css | |
---|---|---|
平均时间(s) | 5.52 | 5.3 |
我们再把large1.css、large2.css … large6.css合并为3个资源large-2in1a.css、large-2in1b.css、large-2in1c.css,每个资源282K,在combined-large-1.html中引用这3个资源:
// combined-large-1.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Parallel Large 1</title> <link rel="stylesheet" type="text/css" media="screen" href="large-2in1a.css" /> <link rel="stylesheet" type="text/css" media="screen" href="large-2in1b.css" /> <link rel="stylesheet" type="text/css" media="screen" href="large-2in1c.css" /> </head> <body> Hello, world! </body> </html>
测试10次,平均加载时间为5.20s。
汇总实验结果如下:
large-6in1.css | large1.css、large2.css … large6.css | large-2in1a.css、... large-2in1c.css | |
---|---|---|---|
平均时间(s) | 5.52 | 5.30 | 5.20 |
从实验1结果可以看出,合并资源和拆分资源对于资源的总加载时间没有显著影响。实验中耗时最少的是拆分成3个资源的情况(5.2s),耗时最多的是合并成一个资源的情况(5.52s),但两者也只不过相差6%。考虑到实验环境具有一定随机性,以及实验重复次数只有10次,这个时间差并不能表征3种场景有明显的时间差异性。
实验 2
继续增加css文件大小。
测试文件:xlarge1.css、xlarge2.css 、xlarge3.css,每个文件1.7M;xlarge-3in1.css,由前面3个css文件合并而成,大小为5.1M。parallel-xlarge.html引用xlarge1.css、xlarge2.css 、xlarge3.css, combined-xlarge.html引用xlarge-3in1.css。
测试过程同上,实验结果如下:
xlarge-3in1.css | xlarge1.css、xlarge2.css、xlarge3.css | |
---|---|---|
平均时间(s) | 37.72 | 36.88 |
这组实验的时间差只有2%,更小了,所以更无法说明合并资源和拆分资源的总加载时间有明显差异性。
实际上,理想情况下,随着资源体积变大,两种资源加载方式所需时间将趋于相同。
从理论上解释,因为HTTP的传输通道是基于TCP连接的,而TCP连接具有慢启动的特性,刚开始时并没有充分利用网络带宽,经过慢启动过程后,逐渐占满可利用的带宽。
对于大资源而言,带宽总是会被充分利用的,所以带宽是瓶颈,即使使用更多的TCP连接,也不能带来速度的提升。资源越大,慢启动所占总的下载时间的比例就越小,绝大部分时间,带宽都是被充分利用的,总数据量相同(拆分资源导致的额外Header在这种情况下完全可以忽略不计),带宽相同,传输时间当然也相同。
实验 3
减小css文件大小。
测试文件:medium1.css、medium2.css … medium6.css,每个文件9.4K;medium-6in1.css,由前面6个css文件合并而成,大小为56.4K。parallel-medium.html引用medium1.css、medium2.css … medium6.css, combined-medium.html 引用 medium-6in1.css。
实验结果如下:
medium-6in1.css | medium1.css、medium2.css … medium6.css | |
---|---|---|
平均时间(ms) | 34.87 | 46.24 |
注意单位变成ms
实验3的时间差是33%,虽然数值上只差12ms。先不多分析,继续看实验4。
实验 4
继续减小css文件大小,至几十字节级别。
测试文件:small1.css、small2.css … small6.css,每个文件28B;small-6in1.css,由前面6个css文件合并而成,大小为173B。parallel-medium.html引用small1.css、small2.css … small6.css, combined-medium.html 引用 small-6in1.css。
实验结果如下:
small-6in1.css | small1.css、small2.css … small6.css | |
---|---|---|
平均时间(ms) | 20.33 | 35 |
实验4的时间差是72%。
提点
对于大资源,是否合并对于加载时间没有明显影响,但拆分资源可以更好的利用浏览器缓存,不会因为某个资源的更新导致所有资源缓存失效。
而资源合并后,任一资源的更新都会导致整体资源的缓存失效。另外还可以利用域名分片技术,将资源拆分部署到不同域名下,既可以分散服务器的压力,又可以降低网络抖动带来的影响。
对于小资源,合并资源往往具有更快的加载速度,但在网络带宽状况良好的情况下,因为提升的时间单位以ms计量,收益可以忽略。
如果网络延迟很大,服务器响应速度又慢,则可以带来一定收益,但在高延迟的网络场景下,又要注意合并资源后可能带来网络往返次数的增加,进而影响到加载时间。
其实,看到这里,是合是分已经不重要了,重要的是我们要知道合分背后的原理是什么,和业务场景是怎样的。