本文作者:IMWeb daihuimi 原文出处:IMWeb社区 未经同意,禁止转载
学习整理了web缓存的一些策略,如有不正确的地方,欢迎指正。
对于浏览器端的缓存来讲,这些规则是在HTTP协议头
和HTML页面的Meta标签
中定义的。他们分别从新鲜度和校验值两个维度来规定浏览器是否可以直接使用缓存中的副本,还是需要去源服务器获取更新的版本。
新鲜度(过期机制):也就是缓存副本有效期。一个缓存副本必须满足以下条件,浏览器会认为它是有效的,足够新的:
满足以上两个情况的一种,浏览器会直接从缓存中获取副本并渲染。
校验值(验证机制):服务器返回资源的时候有时在控制头信息带上这个资源的实体标签Etag(Entity Tag)
,它可以用来作为浏览器再次请求过程的校验标识。如过发现校验标识不匹配,说明资源已经被修改或过期,浏览器需求重新获取资源内容。
缓存命中率:一个缓存的有效性是依照缓存的命中率来度量。它是根据得到数据请求次数与所有请求次数的比率。缓存命中率高意味着有很高的比率数据是从缓存中获取到数据的。
在HTTP请求和响应的消息报头中,常见的与缓存有关的消息报头有:
缓存行为主要由缓存策略决定,而缓存策略由内容拥有者设置。这些策略主要通过特定的HTTP头部来清晰地表达。
当一个用户发起一个静态资源请求的时候,浏览器会通过以下几步来获取资源:
浏览器中的操作对缓存的影响:
指定缓存到期GMT的绝对时间,如果设了max-age
,max-age
就会覆盖expires
。如果expires
到期需要重新请求。
Cache-Control:这个是http 1.1中为了弥补 Expires 缺陷新加入的。
对已缓存的内容进行控制:
Cache-control: public
表示缓存的版本可以被代理服务器或者其他中间服务器识别。
Cache-control: private
意味着这个文件对不同的用户是不同的。只有用户自己的浏览器能够进行缓存,公共的代理服务器不允许缓存。
Cache-control: no-cache
意味着文件的内容不应当被缓存。这在搜索或者翻页结果中非常有用,因为同样的URL,对应的内容会发生变化。
其他相关控制字段:
max-age: 指定缓存过期的相对时间秒数,max-ag=0或者是负值,浏览器会在对应的缓存中把Expires设置为1970-01-01 08:00:00 。
s-maxage: 类似于max-age,只用在共享缓存上,比如proxy.
public: 通常情况下需要http身份验证的情况,响应是不可cahce的,加上public可以使它被cache。
no-cache: 强制浏览器在使用cache拷贝之前先提交一个http请求到源服务器进行确认。这对身份验证来说是非常有用的,能比较好的遵守 (可以结合public进行考虑)。它对维持一个资源总是最新的也很有用,与此同时还不完全丧失cache带来的好处),因为它在本地是有拷贝的,但是在用之前都进行了确认,这样http请求并未减少,但可能会减少一个响应体。
no-store: 告诉浏览器在任何情况下都不要进行cache,不在本地保留拷贝。
must-revalidate: 强制浏览器严格遵守你设置的cache规则。
proxy-revalidate: 强制proxy严格遵守你设置的cache规则。
用法举例: Cache-Control: max-age=3600, must-revalidate
cache:使用本地缓存,不发生请求。
Last-Modified与If-Modified-Since是一对报文头,属于http 1.0。
last-modified是WEB服务器认为对象的最后修改时间,比如文件的最后修改时间,动态页面的最后产生时间。
ETag与If-None-Match是一对报文,属于http 1.1。
ETag可以用来解决这种问题。ETag是一个文件的唯一标志符。就像一个哈希或者指纹,每个文件都有一个单独的标志,只要这个文件发生了改变,这个标志就会发生变化。
ETag机制类似于乐观锁机制,如果请求报文的ETag与服务器的不一致,则表示该资源已经被修改过来,需要发最新的内容给浏览器。
同时使用这两个报文头,在完全匹配If-Modified-Since和If-None-Match即检查完修改时间和Etag之后,如都与服务器的相符,服务器返回304,否则,发送最新内容给浏览器。
Etag/lastModified过程如下:
1.客户端请求一个页面(A)。
2.服务器返回页面A,并在给A加上一个Last-Modified/ETag。
3.客户端展现该页面,并将页面连同Last-Modified/ETag一起缓存。
4.客户再次请求页面A,并将上次请求时服务器返回的Last-Modified/ETag一起传递给服务器。
5.服务器检查该Last-Modified或ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304和一个空的响应体。
304:通过If-Modified-Since If-Match判断资源是否修改,如未修改则返回304,发生了一次请求,但请求内容长度为0,节省了带宽。 如果有多台负载均衡的服务器,不同服务器计算出的Etag可能不同,这样就会造成资源的重复加载。
Etag 主要为了解决 Last-Modified 无法解决的一些问题:
1、一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
2、某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);
3、某些服务器不能精确的得到文件的最后修改时间。
Content-Length:尽管并没有在缓存中明确涉及,Content-Length头部在设置缓存策略时很重要。某些软件如果不提前获知内容的大小以留出足够空间,则会拒绝缓存该内容。
Vary:缓存系统通常使用请求的主机和路径作为存储该资源的键。当判断一个请求是否是请求同样内容时,Vary头部可以被用来提醒缓存系统需要注意另一个附加头部。它通常被用来告诉缓存系统同样注意Accept-Encoding头部,以便缓存系统能够区分压缩和未压缩的内容。
CDN缓存,也叫网关缓存、反向代理缓存。浏览器先向CDN网关发起WEB请求,网关服务器后面对应着一台或多台负载均衡源服务器,会根据它们的负载请求,动态地请求转发到合适的源服务器上。
CDN边缘节点缓存策略因服务商不同而不同,但一般都会遵循http标准协议,通过http响应头中的Cache-control: max-age
的字段来设置CDN边缘节点数据缓存时间。
当客户端向CDN节点请求数据时,CDN节点会判断缓存数据是否过期,若缓存数据并没有过期,则直接将缓存数据返回给客户端;否则,CDN节点就会向源站发出回源请求(back to the source request)
,从源站拉取最新数据,更新本地缓存,并将最新数据返回给客户端。
CDN服务商一般会提供基于文件后缀、目录多个维度来指定CDN缓存时间,为用户提供更精细化的缓存管理。
CDN缓存时间会对“回源率”
产生直接的影响。若CDN缓存时间较短,CDN边缘节点上的数据会经常失效,导致频繁回源,增加了源站的负载,同时也增大的访问延时;若CDN缓存时间太长,会带来数据更新时间慢的问题。开发者需要增对特定的业务,来做特定的数据缓存时间管理。
CDN缓存刷新CDN边缘节点对开发者是透明的,相比于浏览器Ctrl+F5的强制刷新来使浏览器本地缓存失效,开发者可以通过CDN服务商提供的“刷新缓存”接口来达到清理CDN边缘节点缓存的目的。这样开发者在更新数据后,可以使用“刷新缓存”功能来强制CDN节点上的数据缓存过期,保证客户端在访问时,拉取到最新的数据。
当网站更新时,如果CDN节点上数据没有及时更新,即便用户再浏览器使用Ctrl +F5的方式使浏览器端的缓存失效,也会因为CDN边缘节点没有同步最新数据而导致用户访问异常。
CDN架构及原理参见CDN架构以及原理分析
Combo服务,也就是我们在最终拼接生成页面资源引用的时候,并不是生成多个独立的link标签,而是将资源地址拼接成一个url路径,请求一种线上的动态资源合并服务,从而实现减少HTTP请求的需求。
/??fle1,file2,file3,...
的url请求响应就是动态combo服务提供的,它的原理很简单,就是根据url找到对应的多个文件,合并成一个文件来响应请求,并将其缓存,以加快访问速度。
但它也存在一些缺陷:
manifest文件罗列了需要被缓存的文件清单。
CACHE MANIFEST
# wanz app v1
# 指明缓存入口
CACHE:
index.html
style.css
images/logo.png
scripts/main.js
# 以下资源必须在线访问
NETWORK:
login.php
# 如果index.php无法访问则用404.html代替
FALLBACK:
/index.php /404.html
这个过程中有几个问题需要注意:
window.applicationCache.swapCache()
方法来使之生效,出现这种现象的原因是浏览器会先使用离线资源加载页面,然后再去检查manifest是否有更新,所以需要到下次打开页面才能生效。manifest的缓存更新机制阅读
小卡君:HTML5离线存储 初探
localStorage.fresh = “vfresh.org”; //设置一个键值
var a = localStorage.fresh; //获取键值
//API
//清空storage
localStorage.clear();
//设置一个键值
localStorage.setItem(“fresh”,“vfresh.org”);
//获取一个键值
localStorage.getItem(“fresh”);
//return “vfresh.org” //获取指定下标的键的名称(如同Array)
localStorage.key(0);
//return “fresh” //删除一个键值
localStorage.removeItem(“fresh”);
与sessionStroage主要的区别是存储时间和作用域。
另外,严格说来localStorage更像是cookie一类的本地数据存储。但在标准缓存之外,开发人员可以用浏览器的一些功能来实现自定义的客户端“缓存”。
关于localStorage踩坑指南:
jero 使用localStorage必须了解的点
来自alloyteam:如何构建可缓存站点
扩展阅读:
参考: