欢迎大家到腾讯云+社区获取更多腾讯海量技术实践的干货~本文由腾讯IVWEB团队发表于云+社区专栏作者:yangchunwenHTTP协议是前端性能中一个很重要的话题甚至安全。看《web性能权威指南(High Performance Browser Networking)》,分享一下关于HTTP部分的内容,加一点自己的想法。当然没有《HTTP权威指南》那么详细,但是对于理解我们平时做的事情还是很有启发的。预计会出两三篇文章,重点介绍HTTP1.1、HTTPS、HTTP2.0等,本文主要涉及HTTP1.1及其应用。HTTP的历史HTTP0.9第一个版本的HTTP官方称为HTTP0.9,它是一个只有一行的协议,例如:GET/about/(hypertextresponse...)(connectionclosed...)HTTP0.9有几个关键点:client/server,request/responseprotocolASCII协议,运行在TCP/IP链接之上,设计用来传输超文本文档(HTML)每次请求后,server和client之间的连接都会关闭主要使用HTTP传输文本并且不共享TCP连接。HTTP1.0典型的HTTP1.0请求流程如下:GET/rfc/rfc1945.txtHTTP/1.0User-Agent:CERN-LineMode/2.15libwww/2.17b3Accept:*/*HTTP/1.0200OKContent-Type:text/纯内容长度:137582到期时间:1997年12月1日星期四16:00:00GMT最后修改时间:1996年5月1日星期三12:45:26GMT服务器:Apache0.84(超文本响应...)(连接关闭。..)HTTP1.0与之前的版本相比主要有以下变化:请求和响应由于多行头域可以组成一个响应对象,前面增加了一个响应状态行,响应对象不限于超文本。每次请求后,它将关闭。Expires等传输内容的缓存控件会被关闭。将支持内容编码Accept-Encoding、字符集Accept-Charset等协商内容。这时候就会启动request和returnheader的概念,传输不会局限于文本(其他二进制Content)HTTP1.1HTTP1.1是目前大多数应用使用的协议版本。与之前的1.0版本相比,HTTP1.1的语义格式基本保持不变,但增加了许多重要的性能优化:持久连接、分块编码传输、字节范围请求、增强缓存机制、传输编码和请求管道。事实上,持久链接后来被反向移植到HTTP1.0HTTP2.0HTTP2.0的主要目标是提高传输性能,实现低延迟和高吞吐量。HTTP2.0从性能的角度做了很多优化。另一方面,HTTP的高层协议语义不会受到此次版本升级的影响。所有HTTP标头、值及其使用场景都不会改变。任何现有的网站和应用程序都可以在HTTP2.0上运行而无需任何修改。换句话说,当我们的服务器和客户端(比如浏览器)将来支持HTTP2.0时,我们不需要修改标记和做很多额外的编码来利用HTTP2.0的好处,但我们可以享受它带来的好处Deliveredwithlowerlatencyandhighernetworkconnectionutilization!HTTP2.0的内容会在下篇或下篇放出,本文不再过多润色。HTTP1.1和前端性能如前所述,这个版本的HTTP1.1引入了大量增强性能的重要特性,包括:支持连接重用Chunked传输编码以支持流式响应请求管道以支持并行请求处理Byte服务以支持range-basedresourcerequests改进更好的缓存机制这里我们将重点关注前端性能优化中的持久化和管道有些应用程序有持久连接。所谓持久连接就是复用TCP连接,多个HTTP请求共享一个TCP连接。HTTP1.1改变了HTTP协议的语义,默认使用持久连接。换句话说,除非明确告知这样做(通过Connection:close标头),否则服务器将默认保持TCP连接打开。如果您使用的是HTTP1.1,从技术上讲,不需要Connection:Keep-Alive标头,但许多客户端仍然选择添加它。比如我们的浏览器在发起请求的时候,一般都会默认帮我们带上Connection。:保持活动标题。让我们来看看为什么持久连接对我们如此重要。假设一个网页只包含一个HTML文档和一个CSS样式文件,服务器对这两个文件的响应时间分别为40ms和20ms,服务器和浏览器分别位于哈尔滨和深圳,单向光纤两者之间的opticaldelay是28ms(假设是理想状态,实际会比这个大)。首先是获取HTML文档的请求过程:HTML下载完成后,关闭TCP连接。其次,发起CSS资源请求,再次进行TCP握手。可以看到两个HTTP请求都需要经过一个TCP三次握手时间。另外,图中没有体现的是,每个TCP请求都可能经过一个TCP慢启动过程,这是影响传播性能的一个不可忽视的重要因素。如果我们底层的TCP连接被复用,情况会是这样的:显然,在获取CSS的请求中,减少了一次握手往返。最初,每个请求使用两个TCP连接,总延迟为284毫秒。使用持久连接后,避免了握手往返,总延迟减少到228毫秒。两次请求节省56毫秒(一次RTT,往返时间)时间。上面的例子只是一个简单的假设情况,只有一个HTML和一个CSS,但现实世界web中的HTTP请求数量远不止于此,当启用持久连接时,N次请求节省的总延迟时间为(N-1)×RTT。实际上,如果延迟更高、请求更多,性能提升会比此处高得多。事实上,网络延迟越高,请求越多,节省的时间就越多。在实际应用中,节省的总时间可以秒计算。如果每一个HTTP都重新启动一个TCP连接,可想而知会浪费多少时间!HTTP管道持久化HTTP允许我们复用已有的连接来完成多个应用请求,但是多个请求必须严格遵守先进先出(FIFO,先进先出)的队列顺序:发送请求,等待响应完成,然后发送客户端队列中的下一个请求。举个上一节中的长连接的例子,首先,服务器处理完第一个请求后,会发生一个完整的往返:先把response发回来,接着是第二个请求,在第二个请求之后到达服务器期间,服务器处于空闲状态。如果服务器可以在处理完第一个请求后立即开始处理第二个请求呢?甚至,如果服务器可以并行处理两个请求呢?这就是HTTP流水线派上用场的地方,这是对上述工作流的一个小但非常重要的优化。有了HTTP管道,我们的HTTP请求在一定程度上不需要一个一个序列化,而是可以并行化。看起来很理想:如上图,HTML和CSS请求同时到达服务器,服务器同时处理,返回。这一次,通过使用HTTP流水线,减少了两个请求之间的一次往返,将总延迟减少到172毫秒。从一开始没有持久连接和管道的284ms到优化后的172ms,这40%的性能提升完全是因为简单的协议优化。等等,刚才那个例子好像还不够好:既然请求同时到达,同时处理,为什么要先返回HTML,再返回CSS呢?两者不能同时返回吗?理想很丰满,现实却有点骨感。这是HTTP1.1管道的一大限制:HTTP请求不能很好地利用多路复用,一个连接上的多个响应数据不允许交错返回(多通道复用)。因此,在传输下一个响应之前,必须完全返回一个响应。这个管道只允许我们将FIFO队列从客户端迁移到服务器。也就是说请求可以同时到达服务器,服务器也可以同时处理两个文件,但是这两个文件还是要按顺序返回给用户,如下图:HTML和CSS请求同时到达,但是服务器先处理HTML请求,两个请求并行处理。处理HTML需要40ms,处理CSS需要20ms。首先处理CSS请求,但它会被缓冲以等待发送HTML响应。发送完HTML响应后,发送服务器缓冲区中的CSS响应。可以看到,即使客户端同时发送两个请求,CSS资源先准备好,但是服务端也会先发送HTML响应,然后再投递CSS。题外话上面两节给出的例子都提到了HTML和CSS请求是同时到达的。这是书上的例子。其实我个人认为这个例子不是很恰当。在实际的网络中,HTML和其中包含的CSS一般不会同时到达服务器,而普通的瀑布图不会出现这种情况。通常,浏览器在发起CSS等资源请求之前,必须先获取HTML内容。我认为作者只是为了解释原理。个人认为在同一个HTML文档中替换CSS和JS可能更合适。这个问题的原理在于TCP层面的“队头阻塞”。有兴趣可以复习一下计算机网络课程。代价往往是:网络连接不能得到充分利用,造成服务器缓冲开销,可能会在客户端造成更大的延迟。更严重的是,如果前面的请求无限期挂起,或者处理时间过长,后面的所有请求都会被阻塞,等待它完成。因此,在HTTP1.1中,流水线技术的应用非常有限,尽管它的优势是毋庸置疑的。事实上,一些支持流水线的浏览器通常将其作为高级配置选项包含在内,但大多数浏览器禁用它。也就是说,作为一个前端工程师,如果开发的应用是针对普通的浏览器应用,最好不要过度依赖HTTP管道。看来应该期待HTTP2.0中管道的优化了。然而,实际上有一些应用程序很好地利用了HTTP管道。例如,在WWDC2012上,苹果工程师分享了一个HTTP优化的案例,取得了很好的效果:通过使用HTTP持久连接和管道,重用iTunes现有的TCP连接,使得低网速用户的性能提升到原来的3倍原来的!事实上,如果要充分利用管道的好处,必须确保以下条件:HTTP客户端支持管道HTTP服务器支持管道应用程序可以处理中断的连接和恢复应用程序可以处理中断的请求幂等问题applicationcanprotectitself不受有问题的代理的影响因为iTunes服务器和客户端是开发者控制的应用程序,它们满足上述条件。这可能会给开发混合应用程序或开发浏览器以外的Web应用程序的前端工程师一些启发。如果你开发的网站是针对使用各种浏览器的用户,你可能会不知所措。由于HTTP1.1管道的上述缺点,使用多个TCP连接未得到充分利用。那么问题来了:假设不使用HTTP管道,那么我们所有的HTTP请求只能通过长连接一个接一个地串行返回。这有多慢?事实上,现阶段的浏览器厂商已经采取了另一种方法来解决HTTP1.1管道的缺陷:允许我们并行打开多个TCP会话。至于有多少,大家之前可能都见过:4到8不等。这就是前端工程师所熟悉的浏览器真正只允许从同一个服务器并行加载4到8个资源的理解来自。HTTP长连接虽然帮助我们解决了TCP连接复用的问题,但是目前的HTTP管道无法实现多个请求结果的交错返回,所以浏览器只能开启多个TCP连接来实现并行资源加载。目的。只能说这是绕过应用协议(HTTP)限制的权宜之计。可以这样打个比方:一根水管不能同时输送多种液体,所以每种液体只能开一根输送管。不受干扰地到达目的地并整理自己?再次期待HTTP2.0。为什么这里的连接数是4到8是多方平衡的结果:数字越大,客户端和服务端占用的资源越多(高并发访问的服务器TCP连接造成的系统开销不容忽视),每台主机4到8个连接只是大家觉得比较安全的一个数字。前面在域名分区中提到,浏览器和服务器之间只能并发连接4到8个TCP连接,也就是同时下载4到8个资源,够吗?纵观我们现在的大部分网站,动不动就有几十个JS和CSS,一次六个,会造成大量资源排队等候;另外,只下载了6个资源,带宽利用率也很低。打个比方,一个工厂安装了100根水管,但一次只能用6根接水,既慢又浪费水管!因此,我们在前端性能优化上有一个最佳实践:使用域名分区!是啊,为什么要把自己限制在一个主机上?我们可以手动将所有资源分配到多个子域。由于主机名不同,我们可以突破浏览器的连接限制,达到更高的并行度。这样就“骗”了浏览器,使浏览器与服务器并行传输的次数增加。使用的域分区越多,并行性就越强!然而,域分区也是有代价的!在实践中,域名分区经常被滥用。例如,假设您的应用程序针对2G网络上的手机用户。你分配了几个域名,同时加载了十几二十个CSS和JS。这里的问题是每个域名都会造成额外的DNS查询开销。这是额外的机器资源开销和额外的网络延迟的成本。2G网络的DNS查询不像你们公司的电脑。反之,同时加载多个资源可能会延迟几秒。从2G网络带宽小得可怜的情况来看,结果往往是带宽被占满。每个资源都下载很慢,手机耗电加速。所以在一些低带宽、高延迟的场景下,比如2G手机网络,如果做了域名分区,不仅不会提升前端性能,反而会成为性能杀手。域名分区是一种合理但不完善的优化方法。最合适的方式是从最小的分区数开始(不分区),然后一个一个地增加分区,衡量分区后对应用的影响,从而得到一个最优的域名数。连接和扁平化在我们的前端性能优化中有这么一个所谓的最佳实践原则:合并打包JS和CSS文件,制作CSS精灵。现在我们应该知道为什么要这样做了,其实是因为现在HTTP1.1流水线太弱了,这两种技术的效果就好像隐式启用了HTTP流水线一样:将多个响应的数据一个接一个地拼接在一起,附加网络消除了延迟。其实就是改进pipeline,放到application里面。也许在HTTP2.0时代,前端工程师就不用做这样的工作了吧?(HTTP2.0的内容下篇再说)当然,连接拼接技术也是有代价的。例如,对于CSS精灵,浏览器必须分析整个图像,即使实际显示的只是其中的一小部分,并且始终将整个图像保存在内存中。浏览器没有办法从内存中删除未显示的部分。再者,由于合并了JS和CSS,结果一般都是体积增大。在带宽有限(比如2G)的环境下,下载时间会变长,一般会导致页面渲染时间延迟等后果。由于JavaScript和CSS处理器不允许增量执行,因此JavaScript和CSS的解析和执行将不得不等到整个文件下载完毕。包文件有多大?不幸的是,没有理想的尺寸。但是,GooglePageSpeed团队的测试表明,30~50KB(压缩后)是每个JavaScript文件的合适大小范围:足够大可以减少小文件带来的网络延迟,也可以保证增量并分层执行。确切的结果可能因应用程序类型和脚本数量而异。资源中嵌入的JavaScript和CSS代码可以通过适当的脚本和样式块直接放置在页面中,而图片甚至音频或PDF文件可以通过数据URI(data:mediatype,data)嵌入到页面中。上述方法称为资源嵌入。嵌入资源是另一种非常流行的优化方法。在文档中嵌入资源可以减少请求的数量。尤其是在2G网络等情况下,嵌入资源可以有效降低多次请求带来的延迟。2G中的一些实践可以参考这篇文章。当然,也有缺点:嵌入式资源不能被浏览器、CDN或其他缓存代理缓存为单独的资源。如果同一个资源嵌入到多个页面中,资源将随着每个页面加载而加载,从而增加每个页面的整体大小。如果内嵌资源更新了,那么之前出现过的所有页面都会被宣告无效,客户端会重新从服务端获取。对图片等非文本资源进行base64编码会导致开销明显增加:编码后的资源大小比原始大小大33%!谷歌的砖给了一些经验:只考虑嵌入1~2KB以下的资源,因为小于这个标准的资源往往会导致HTTP开销比本身高。如果文件很小,只有几个页面使用,可以考虑嵌入。理想情况下,只使用一次的资源如果文件很小但需要在多个页面中重复使用,则应考虑集中打包如果小文件需要经常更新,则不要嵌入通过减少协议开销来减少sizeoftheHTTPcookieMinimizationSummary本文介绍了HTTP1.1在前端性能优化方面的一些应用,其中一些是为了绕过HTTP1.1的限制而不得不做的,比如资源合并、压缩、嵌入等,这一切都可以说是HTTP2.0到来之前解决问题的一些“黑魔法”。当然,HTTP1.1及其使用远没有本文描述的那么简单。我只是浓缩了一部分内容。有兴趣的可以研究一下《HTTP权威指南》。问答BDD框架前端如何搭建?相关阅读综合进阶H5直播(下)NodeJs内存管理WebGL纹理配色原理【每日课程推荐】机器学习实战!快速介绍网络广告业务及CTR相关知识。本文已获作者授权腾讯云+社区发布。更多原创文章,请点击搜索并关注公众号“云家社区”。技术课程大礼包!海量技术实战经验,尽在云家社区!
