找回密码
 会员注册
查看: 25|回复: 0

HTTP请求之合并与拆分技术详解

[复制链接]

2万

主题

0

回帖

6万

积分

超级版主

积分
64454
发表于 2024-9-20 17:21:54 | 显示全部楼层 |阅读模式
作者:darminzhou,腾讯CSIG前端开发工程师导语:HTTP/2中,是否还需要减少请求数?来看看实验数据吧。1.背景随着网站升级HTTP/2协议,在浏览页面时常常会发现页面的请求数量很大,尤其是小请求,经典的雅虎前端性能优化军规中的第1条就是减少请求数,在HTTP/1.1时代合并雪碧图是这种场景减少请求数的一大途径,但是现在这些是使用HTTP/2协议传输的,这种方式是否也适用?另外,在都使用HTTP/2的情况,在浏览器并发这么多小请求时,是否会影响其他静态资源的拉取速度(例如页面js文件的请求耗时)?基于上面问题的思考,本文进行了一个简单的实验,尝试通过数据来分析HTTP中的合并与拆分,以及并发请求是否影响其他请求。通过这次的实验我们对比了以下几个不同HTTP场景的耗时数据:HTTP/1.1合并VS拆分HTTP/1.1VSHTTP/2并发请求HTTP/2合并VS拆分浏览器并发HTTP/2请求数(大量VS少量)时,其他请求的耗时2.实验准备理论:合并与拆分都是HTTP请求优化的常用方法,合并主要为了减少请求数,可以减少多次建立TCP连接耗时,不过相对的,缓存命中率会受到影响;拆分主要为了利用并发能力,浏览器可以并发多个TCP连接,还可以结合HTTP/1.1中的长链接,不过受HTTP队头阻塞影响,并发能力并不强,于是HTTP/2协议出现,使用多路复用、头部压缩等技术很好的解决了HTTP队头阻塞问题,实现了较强的并发能力。而HTTP/2由于基于TCP,依然无法绕过TCP队头阻塞问题,于是又出现了HTTP/3,不过本文并不讨论HTTP/3,有兴趣的同学可以自行Google。实验环境:为了避免自己搭服务器可能存在的性能影响,实验中的资源数据使用腾讯云的COS存储,并开启了CDN加速。3.实验分析第一个实验:有2个HTML。1个HTML中并发加载361张小,记录所有加载完成时的耗时;另1个HTML加载合并图并记录其耗时。并分别记录基于HTTP/1.1和HTTP/2协议的不同限速情况的请求耗时情况。每个场景测试5次,每次都间隔一段时间避免某一时间段网络不好造成的数据偏差,最后计算平均耗时。实验数据:3.1HTTP/1.1合并VS拆分根据上面实验数据,抽出其中HTTP/1.1的合并和拆分的数据来看,很明显拆分的多个小请求耗时远大于合并的请求,且网速较低时差距更大。HTTP/1.1合并请求的优化原理简单看下HTTP请求的主要过程:DNS解析(T1)->建立TCP连接(T2)->发送请求(T3)->等待服务器返回首字节(TTFB)(T4)->接收数据(T5)。从上面请求过程中,可以看出当多个请求时,请求中的DNS解析、建立TCP连接等步骤都会重复执行多遍。那么如果合并N个HTTP请求为1个,理论上可以节省(N-1)*(T1+T2+T3+T4)的时间。当然实际场景并没有这么理想,比如浏览器会缓存DNS信息,因此不是每次请求都需要DNS解析;比如HTTP/1.1keep-alive的特性,使HTTP请求可以复用已有TCP连接,所以并不是每个HTTP请求都需要建立新的TCP连接;再比如浏览器可以并行发送多个HTTP请求,同样可能影响到资源的下载时间,而上面的分析显然只是基于同一时刻只有1个HTTP请求的场景。感兴趣深入了解的可以参考网上一篇HTTP/1.1详细实验数据,其结论是:当文件体积较小的时候,(网络延迟低的场景下)合并后的文件的加载耗时明显小于加载多个文件的总耗时;当文件体积较大的时候,合并请求对于加载耗时没有明显的影响,且拆分资源可以提高缓存命中率。但是注意有特殊的场景,由于合并资源后可能导致网络往返次数的增加,当网络延迟很大时,是会增大耗时的(参考TCP拥塞控制)。【扩展:TCP拥塞控制】TCP中包含一种称为拥塞控制的机制,拥塞控制的主要工作是确保网络不会同时被过多的数据传输导致过载。当前拥塞控制的方法有许多,主要原理是慢启动,例如,开始阶段只发送一点数据,观察是否能通过,如果能接收方将确认发送回发送方,只要所有数据都得到确认,发送方就在下次RTT时将发送数据量加倍,直到观察到丢包事件(丢包意味着过载,需要后退),每次发送的数据量即拥塞窗口,就是这样动态调整拥塞窗口来避免拥塞。拥塞控制机制对每个TCP连接都是独立的。3.2HTTP/1.1VSHTTP/2并发请求抽出实验数据中的HTTP/1.1和HTTP/2并发请求来对比分析,可以看出HTTP/2的并发总耗时明显优于HTTP/1.1,且网速越差,差距越大。HTTP/2多路复用和头部压缩的原理多路复用:在一个TCP链接中可以并行处理多个HTTP请求,主要是通过流和帧实现,一个流代表一个HTTP请求,每个HTTP资源拆分成一个个的帧按顺序进行传输,不同流的帧可以穿插传输,最终依然能根据流ID组合成完整资源,以此实现多路复用。帧的类型有11种,例如headers帧(请求头/响应头),data帧(body),settings帧(控制传输过程的配置信息,例如流的并发上限数、缓冲容量、每帧大小上限)等等。头部压缩:为了节约传输消耗,通过压缩的方式传输同一个TCP链接中不同HTTP请求/响应的头部数据,主要利用了静态表和动态表来实现,静态表规定了常用的一些头部,只用传输一个索引即可表示,动态表用于管理一些头部数据的缓存,第一次出现的头部添加至动态表中,下次传输同样的头部时就只用传输一个索引即可。由于基于TCP,头部帧的发送和接收后的处理顺序是保持一致的,因此两端维护的动态表也就保证一致。多路复用允许一次TCP链接处理多次HTTP请求,头部压缩又大大减少了多个HTTP请求可能产生的重复头部数据消耗。因此HTTP/2可以很好的支持并发请求,感兴趣可以深入HTTP/2浏览器源码分析。【扩展:队头阻塞】HTTP/2解决了HTTP/1.1中HTTP层面(应用层)队头阻塞的问题,但是由于HTTP/2仍然基于TCP,因此TCP层面的队头阻塞依然存在。HTTP/3使用QUIC解决了TCP队头阻塞的问题。感兴趣可以看看队头阻塞这篇文章。HTTP层面的队头阻塞在于,HTTP/1.1协议中同一个TCP连接中的多个HTTP请求只能按顺序处理,方式有两种标准,非管道化和管道化两种,非管道化方式:即串行执行,请求1发送并响应完成后才会发送请求2,一但前面的请求卡住,后面的请求就被阻塞了;管道化方式:即请求可以并行发出,但是响应也必须串行返回(只提出过标准,没有真正应用过)。TCP层面的队头阻塞在于,TCP本身不知道传输的是HTTP请求,TCP只负责传递数据,传递数据的过程中会将数据分包,由于网络本身是不可靠的,TCP传输过程中,当存在数据包丢失的情况时,顺序排在丢失的数据包之后的数据包即使先被接收也不会进行处理,只会将其保存在接收缓冲区中,为了保证分包数据最终能完整拼接成可用数据,所丢失的数据包会被重新发送,待重传副本被接收之后再按照正确的顺序处理它以及它后面的数据包。But,由于TCP的握手协议存在,TCP相对比较可靠,TCP层面的丢包现象比较少见,需要明确的是,TCP队头阻塞是真实存在的,但是对Web性能的影响比HTTP层面队头阻塞小得多,因此HTTP/2的性能提升还是很有作用的。HTTP/2中存在TCP的队头阻塞问题主要由于TCP无法记录到流id,因为如果TCP数据包携带流id,所丢失的数据包就只会影响数据包中相关流的数据,不会影响其他流,所以顺序在后的其他流数据包被接收到后仍可处理。出于各种原因,无法改造TCP本身,因此为了解决HTTP/2中存在的TCP对头阻塞问题,HTTP/3在传输层不再基于TCP,改为基于UDP,在UDP数据帧中加入了流id信息。3.3HTTP/2合并VS拆分由于HTTP/2支持多路复用和头部压缩,是不是原来HTTP/1.1中的合并请求的优化方式就没用了,在HTTP/2中合并雪碧图有优化效果吗?抽出HTTP/2的合并和拆分的数据来看,拆分的多个小请求耗时仍大于合并的请求,不过差距明显缩小了很多。那么为什么差距还是挺大呢?理论上HTTP/2的场景下,带宽固定,总大小相同的话,拆分的多个请求最好的情况应该是接近合并的总耗时的才对吧。分析一下,因为是复用一个TCP连接,所以首先排除重复DNS查询、建立TCP连接这些影响因素。那么再分析一下资源大小的影响:本身合并的(516kB)就比拆分的361张小总大小(总646kB)要小。拆分的很多个小请求时,虽然有头部压缩,但是请求和响应中的头部数据以及一些settings帧数据还是会多一些。通过查看chrome的transferred可以知道小最终总传输数据741kB,说明除body外多传输了将近100kB的数据。结合上面两点,理论上拆分的小总耗时应该是合并的耗时的(741/516=)1.44倍。但是很明显测试中各网速场景下拆分的小总耗时与合并耗时的比值都大于1.44这个理论值(2.62、2.96、1.84)。其中的原因这里有两点推测:并发多个请求的总耗时计算的是所有请求加载完的耗时,每个请求都有发送和响应过程,其中分为一个个帧的传输过程,只要其中某小部分发生阻塞,就会拖累总耗时情况。浏览器在处理并发请求过程存在一定的调度策略而导致。推测的依据来自Chrome开发者工具中的Waterfall,可以看到很多并发请求的QueueingTime、StalledTime很高,说明浏览器不会在一开始就并行发送所有请求。不过这里只属于猜测,还未深入探究。3.4浏览器并发HTTP/2请求数(大量VS少量)时,其他请求的耗时第二个实验:在HTML中分别基于HTTP/2加载360+张小、130+张小、20+张小、0张小,以及1张大和1个js文件,大在DOM中放在所有小的后面,都是同域名的,js文件是不同域名的,然后记录大和脚本的耗时,同样也是利用Chrome限速工具在不同的网络限速下测试(不过这个连的WIFI与第一个实验中不同,无限速时的网速略微不同)。这个实验主要用于分析并发请求过多时是否会影响其他请求的访问速度。实验数据:从实验数据中可以看出,并发数量不会影响js的加载速度,无限速时无论并发请求有多少,脚本加载都只要0.12s左右。很明显对大的加载速度有影响,可以看到并发量从大到小时,大的耗时明显一次减少。但是其中也有几个反常的数据:Fast3G和Slow3G的网速限制下,无小时的js加载耗时明显高于有并发小请求的js加载耗时。这是为啥?我们推测这里的原因是,由于和js不同域名,分别在两个TCP连接中传输,两个TCP是分享总网络带宽的,当有多个小时,小在DOM前优先级高,js和小分享网络带宽,js体积较大占用带宽较多,而无小时,js是和大分享网络带宽,js占用带宽比率变小,因此在限速时带宽不够的情况下表现出这样的反常数据。4.实验结论HTTP/1.1中合并请求带来的优化效果还是明显的。对于多并发请求的场景HTTP/2比HTTP/1.1的优势也是挺明显的。不过也要结合具体环境,HTTP/2中由于复用1个TCP链接,如果并发中某一个大请求资源丢包率严重,可能影响导致整个TCP链路的流量窗口一直很小,而这时HTTP/1.1中可以开启多个TCP链接可能其他资源的加载速度更快?当然这也只是个人猜测,没有具体实验过。HTTP/2中合并请求耗时依然会比拆分的请求总耗时低一些,但是相对来说效果没有HTTP/1.1那么明显,可以多结合其他因素,例如拆分的必要性、缓存命中率需求等,综合决策是否合并或拆分。网速较好的情况下,非同域名下的请求相互间不受影响,同域名的并发请求,随着并发量增大,优先级低的请求耗时也会增大。不过,本文中的实验环境较为有限,说不定换了一个环境会得到不同的数据和结论?比如不同的浏览器(Firefox、IE等)、不同的操作系统(Windows、Linux等)、不同的服务端能力以及不同测试资源等等,大家感兴趣也可以抽点时间试一试。5.其他思考以上讨论主要针对低计算量的静态资源,那么高计算量的动态资源的请求呢,(例如涉及鉴权、数据库查询之类的),合并vs.拆分?关于我们我们团队主要致力于前端相关技术的研究和在腾讯业务的应用,团队内部每周有内部分享会,有兴趣的读者可以加入我们或者参与一起讨论,微信:darminzhou;camdyzeng。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-27 01:59 , Processed in 1.633760 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表