当前位置: 首页 > 科技观察

HTTP-2和WEB性能优化(一)

时间:2023-03-11 21:32:32 科技观察

今年2月,谷歌宣布将于2016年初放弃对SPDY的支持,随后谷歌自家支持SPDY协议的服务转为HTTP/2。今年5月14日,HTTP/2正式发布为RFC7540。目前,Chrome40+和Firefox36+浏览器都正式支持HTTP/2;服务器方面,大名鼎鼎的Nginx表示将在今年年底正式支持HTTP/2。不得不说,近年来WEB技术突飞猛进,呈爆发式发展。昨天我觉得HTTP/2很遥远,但今天它无处不在。对于新鲜事物,有些人不愿意接受,认为何必如此;有些人会盲目崇拜它,认为它是拯救一切的救世主。HTTP/2到底会给前端带来什么,什么都没有?或者,正如某些人所说,“淘汰前端的那些优化技巧”?我打算写一系列文章来尝试回答这个问题,今天是**篇。提出问题我们知道,一个页面通常由一个HTML文档和多个资源组成。有一些很重要的资源,比如头部的CSS和关键的JS,如果长时间不加载,会阻塞页面渲染或者导致用户无法交互,体验很差贫穷的。如何让重要的资源加载得更快,是我这篇文章要讨论的问题。HTTP/1分析我们首先考虑资源外链的情况。通常外链资源部署在CDN上,用户可以从离自己最近的节点获取数据。通常,文本文件使用gzip压缩,实际传输大小是文件大小的几分之一。在服务器端托管静态资源的效率通常很高,而服务器端的处理时间几乎可以忽略不计。在忽略网络因素、传输大小和服务器端处理时间后,用户何时能加载完外部资源很大程度上取决于请求何时能发送出去,这主要受以下三个因素的影响:浏览器阻塞(Stalled):浏览器会出于某种原因阻止该请求。比如rfc2616中规定一个浏览器只能同时有2个连接到一个域名(这个限制在HTTP/1.1的修订版中去掉了,详见rfc7230,因为浏览器实际上放宽了限制later),超过浏览器***连接数限制,后续请求会被阻塞。再比如,现代浏览器在加载同一个域名的多个HTTPS资源时,会故意等待第一个TLS连接建立后再请求其他资源;DNS查找(DNSLookup):浏览器需要知道目标服务器的IP才能建立连接。将域名解析为IP的系统是DNS。DNS查询结果通常会缓存一段时间,但第一次访问或缓存失效时可能需要几十到几百毫秒;建立连接(Initialconnection):HTTP基于TCP协议,浏览器最快只能在第三次握手时搭载HTTP请求报文。这个过程通常需要数百毫秒;当然,我们一般都会为静态资源设置一个长期缓存头。只要用户不清除浏览器缓存或刷新,当他第二次访问我们的网页时,会直接从本地缓存中获取静态资源,不会产生网络请求;有了协商字段If-Modified-Since或If-None-Match,服务端会响应304状态码给没有变化的资源,告诉浏览器从本地缓存中获取资源。304请求没有文本而且很小。也就是说,资源外部链接的特点是第一次慢,第二次快。我们来看看资源内联的情况。直接在HTML中内联CSS和JS文件内容的方案,在用户第一次访问时无疑具有速度优势。但是通常我们很少缓存HTML页面。这种方案会导致内联资源无法使用浏览器缓存,以后每次访问都是浪费。解决方案很早以前,一些网站就开始为初次使用的用户内联资源,在页面加载完成后异步加载这些资源的外链版本,并记录一个cookie标记,表示用户已经到过。当用户再次访问该页面时,服务器可以输出只有外链版本的页面,减小页面大小。除了有点浪费流量(一个资源,内联和外链加载两次),这个方案基本可以达到更快加载重要资源的效果。但是在流量更有价值的移动端,我们需要继续完善这个方案。考虑到手机端浏览器都支持localStorage,第一次inline引入的资源可以缓存起来供后续使用。缓存更新机制可以通过在cookie中存储版本号来实现。这样,服务端收到请求后,首先会检查Cookie头中的版本标签:如果标签不存在或版本不匹配,则内联输出资源,并提供当前版本标签。页面执行时,内联资源会存储在localStorage中,资源版本标签会存储在cookie中;如果标签匹配,将输出一个JavaScript片段来读取和使用localStorage中的资源;由于cookie的内容需要越小越好,所以一般只存储总版本号。这将导致页面上的任何资源更改都会更改总版本号,从而忽略客户端的所有localStorage缓存。为了解决这个问题,我们可以继续改进我们的方案:cookie中只保存了用户的唯一标识,用户和资源的对应关系存在于服务端。服务器收到请求后,根据用户ID计算需要更新哪些资源,从而输出更有针对性的HTML文档。要将此方案付诸实际使用,必须处理一系列异常情况,例如JS/Cookie/localStorage被禁用;本地存储已满;localStorage内容损坏或丢失等。综合考虑成本和实际收益,建议只在移动端项目中使用该方案。HTTP/2对于HTTP/2,要解决前面的问题太容易了,打开“ServerPush”就可以了。HTTP/2的多路复用特性使得在一个连接上同时开启多个流,双向传输数据成为可能。服务器推送是指服务器在发送页面HTML时可以主动推送其他资源,而不是等待浏览器解析到相应位置,发起请求然后响应。另外,服务器主动推送的资源并没有内嵌在页面中,它们有自己独立的URL,可以被浏览器缓存,当然也可以被其他页面使用。服务端可以主动推送,客户端也有选择接收与否的权利。如果服务器推送的资源已经被浏览器缓存,浏览器可以通过发送RST_STREAM帧来拒绝。可见HTTP/2ServerPush可以很好的解决“如何尽快加载重要资源”的问题。一旦流行起来,就可以取代之前介绍的HTTP/1时代的优化方案。