HTTP系列2 HTTP缓存

经过网络获取内容既速度缓慢又开销巨大。较大的响应须要在客户端与服务器之间进行屡次往返通讯,这会延迟浏览器得到和处理内容的时间,还会增长访问者的流量费用。所以,缓存并重复利用以前获取的资源的能力成为性能优化的一个关键方面。css

缓存类型

(私有)浏览器缓存

私有缓存只能用于单独用户。你可能已经见过浏览器设置中的“缓存”选项。浏览器缓存拥有用户经过 HTTP 下载的全部文档。这些缓存为浏览过的文档提供向后/向前导航,保存网页,查看源码等功能,能够避免再次向服务器发起多余的请求。它一样能够提供缓存内容的离线浏览。html

(共享)代理缓存

共享缓存能够被多个用户使用。例如,ISP 或你所在的公司可能会架设一个 web 代理来做为本地网络基础的一部分提供给用户。这样热门的资源就会被重复使用,减小网络拥堵与延迟。
HTTPCachtType.pngweb

缓存控制

Cache-control 头

HTTP/1.1定义的 Cache-Control 头用来区分对缓存机制的支持状况, 请求头和响应头都支持这个属性。经过它提供的不一样的值来定义缓存策略。算法

禁止进行缓存

缓存中不得存储任何关于客户端请求和服务端响应的内容。每次由客户端发起的请求都会下载完整的响应内容。浏览器

Cache-Control: no-store
Cache-Control: no-cache, no-store, must-revalidate

强制确认缓存

以下头部定义,此方式下,每次有请求发出时,缓存会将此请求发到服务器(译者注:该请求应该会带有与本地缓存相关的验证字段),服务器端会验证请求中所描述的缓存是否过时,若未过时(注:实际就是返回304),则缓存才使用本地缓存副本。缓存

Cache-Control: no-cache

私有缓存和公共缓存

"public" 指令表示该响应能够被任何中间人(译者注:好比中间代理、CDN等)缓存。若指定了"public",则一些一般不被中间人缓存的页面(译者注:由于默认是private)(好比 带有HTTP验证信息(账号密码)的页面 或 某些特定影响状态码的页面),将会被其缓存。性能优化

而 "private" 则表示该响应是专用于某单个用户的,中间人不能缓存此响应,该响应只能应用于浏览器私有缓存中。服务器

Cache-Control: private
Cache-Control: public

缓存过时机制

过时机制中,最重要的指令是 "max-age=<seconds>",表示资源可以被缓存(保持新鲜)的最大时间。相对Expires而言,max-age是距离请求发起的时间的秒数。针对应用中那些不会改变的文件,一般能够手动设置必定的时长以保证缓存有效,例如图片、css、js等静态资源。网络

注:
当设置max-age时,empires头会被忽略。
相应的,If-Modified-Since 只能够用在 GET 或 HEAD 请求中,当与 If-None-Match 一同出现时,它会被忽略掉,除非服务器不支持 If-None-Match。函数

Cache-Control: must-revalidate

新鲜度

理论上来说,当一个资源被缓存存储后,该资源应该能够被永久存储在缓存中。因为缓存只有有限的空间用于存储资源副本,因此缓存会按期地将一些副本删除,这个过程叫作缓存驱逐。另外一方面,当服务器上面的资源进行了更新,那么缓存中的对应资源也应该被更新,因为HTTP是C/S模式的协议,服务器更新一个资源时,不可能直接通知客户端及其缓存,因此双方必须为该资源约定一个过时时间,在该过时时间以前,该资源(缓存副本)就是新鲜的,当过了过时时间后,该资源(缓存副本)则变为陈旧的。驱逐算法用于将陈旧的资源(缓存副本)替换为新鲜的,注意,一个陈旧的资源(缓存副本)是不会直接被清除或忽略的,当客户端发起一个请求时,缓存检索到已有一个对应的陈旧资源(缓存副本),则缓存会先将此请求附加一个If-None-Match头,而后发给目标服务器,以此来检查该资源副本是不是依然仍是算新鲜的,若服务器返回了 304 (Not Modified)(该响应不会有带有实体信息),则表示此资源副本是新鲜的,这样一来,能够节省一些带宽。若服务器经过 If-None-Match 或 If-Modified-Since判断后发现已过时,那么会带有该资源的实体内容返回。

下面是上述缓存处理过程的一个图示:

HTTPStaleness.png

对于含有特定头信息的请求,会去计算缓存寿命。好比Cache-control: max-age=N的请求头,相应的缓存的寿命就是N。一般状况下,对于不含这个属性的请求则会去查看是否包含Expires属性,经过比较Expires的值和头里面Date属性的值来判断是否缓存还有效。若是max-age和expires属性都没有,找找头里的Last-Modified信息。若是有,缓存的寿命就等于头里面Date的值减去Last-Modified的值除以10(注:根据rfc2626其实也就是乘以10%)。

缓存失效时间计算公式以下:

expirationTime = responseTime + freshnessLifetime - currentAge

上式中,responseTime 表示浏览器接收到此响应的那个时间点。

加速资源

更多地利用缓存资源,能够提升网站的性能和相应速度。为了优化缓存,过时时间设置得尽可能长是一种很好的策略。对于按期或者频繁更新的资源,这么作是比较稳妥的,可是对于那些长期不更新的资源会有点问题。这些固定的资源在必定时间内受益于这种长期保持的缓存策略,但一旦要更新就会很困难。特指网页上引入的一些js/css文件,当它们变更时须要尽快更新线上资源。

web开发者发明了一种 Steve Sounders 称做加速(译者注:revving)的技术 。不频繁更新的文件会使用特定的命名方式:在URL后面(一般是文件名后面)会加上版本号。加上版本号后的资源就被视做一个彻底新的独立的资源,同时拥有一年甚至更长的缓存过时时长。可是这么作也存在一个弊端,全部引用这个资源的地方都须要更新连接。web开发者们一般会采用自动化构建工具在实际工做中完成这些琐碎的工做。当低频更新的资源(js/css)变更了,只用在高频变更的资源文件(html)里作入口的改动。

这种方法还有一个好处:同时更新两个缓存资源不会形成部分缓存先更新而引发新旧文件内容不一致。对于互相有依赖关系的css和js文件,避免这种不一致性是很是重要的。

HTTPRevved.png

加在加速文件后面的版本号不必定是一个正式的版本号字符串,如1.1.3这样或者其余固定自增的版本数。它能够是任何防止缓存碰撞的标记例如hash或者时间戳。

带Vary头的响应

Vary 是一个HTTP响应头部信息,它决定了对于将来的一个请求头,应该用一个缓存的回复(response)仍是向源服务器请求一个新的回复。它被服务器用来代表在 content negotiation algorithm(内容协商算法)中选择一个资源表明的时候应该使用哪些头部信息(headers).

在响应状态码为 304 Not Modified 的响应中,也要设置 Vary 首部,并且要与相应的 200 OK 响应设置得如出一辙。

当缓存服务器收到一个请求,只有当前的请求和原始(缓存)的请求头跟缓存的响应头里的Vary都匹配,才能使用缓存的响应。

HTTPVary.png

使用vary头有利于内容服务的动态多样性。例如,使用Vary: User-Agent头,缓存服务器须要经过UA判断是否使用缓存的页面。若是须要区分移动端和桌面端的展现内容,利用这种方式就能避免在不一样的终端展现错误的布局。另外,它能够帮助google或者其余搜索引擎更好地发现页面的移动版本,而且告诉搜索引擎没有引入Cloaking。

Vary: User-Agent

对于服务器而言, 资源文件可能不止一个版本, 好比说压缩和未压缩, 针对不一样的客户端, 一般须要返回不一样的资源版本. 好比说老式的浏览器可能不支持解压缩, 这个时候, 就须要返回一个未压缩的版本; 对于新的浏览器, 支持压缩, 返回一个压缩的版本, 有利于节省带宽, 提高体验. 那么怎么区分这个版本呢, 这个时候就须要Vary了.

服务器经过指定Vary: Accept-Encoding, 告知代理服务器, 对于这个资源, 须要缓存两个版本: 压缩和未压缩. 这样老式浏览器和新的浏览器, 经过代理, 就分别拿到了未压缩和压缩版本的资源, 避免了都拿同一个资源的尴尬.

Vary:Accept-Encoding,User-Agent

如上设置, 代理服务器将针对是否压缩和浏览器类型两个维度去缓存资源. 如此一来, 同一个url, 就能针对PC和Mobile返回不一样的缓存内容.

由于移动版和桌面的客户端的请求头中的User-Agent不一样, 缓存服务器不会错误地把移动端的内容输出到桌面端到用户。

定义最佳缓存策略

图片描述

按照以上决策树为您的应用使用的特定资源或一组资源肯定最佳缓存策略。在理想的状况下,您的目标应该是在客户端上缓存尽量多的响应,缓存尽量长的时间,而且为每一个响应提供验证令牌,以实现高效的从新验证。

缓存检查清单

不存在什么最佳缓存策略。您须要根据通讯模式、提供的数据类型以及应用特定的数据更新要求,为每一个资源定义和配置合适的设置,以及总体的“缓存层次结构”。

在制定缓存策略时,您须要牢记下面这些技巧和方法:

  • 使用一致的网址:若是您在不一样的网址上提供相同的内容,将会屡次获取和存储这些内容。提示:请注意,网址区分大小写。
  • 确保服务器提供验证令牌 (ETag):有了验证令牌,当服务器上的资源未发生变化时,就不须要传送相同的字节。
  • 肯定中间缓存能够缓存哪些资源:对全部用户的响应彻底相同的资源很是适合由 CDN 以及其余中间缓存进行缓存。
  • 为每一个资源肯定最佳缓存周期:不一样的资源可能有不一样的更新要求。为每一个资源审核并肯定合适的 max-age。
  • 肯定最适合您的网站的缓存层次结构:您能够经过为 HTML 文档组合使用包含内容指纹的资源网址和短期或 no-cache 周期,来控制客户端获取更新的速度。
  • 最大限度减小搅动:某些资源的更新比其余资源频繁。若是资源的特定部分(例如 JavaScript 函数或 CSS 样式集)会常常更新,能够考虑将其代码做为单独的文件提供。这样一来,每次获取更新时,其他内容(例如变化不是很频繁的内容库代码)能够从缓存获取,从而最大限度减小下载的内容大小。
相关文章
相关标签/搜索