Open PaulChess opened 3 years ago
缓存是一种保存资源副本并在下一次请求时直接使用该副本的技术。当 web 缓存发现请求的资源已经被存储,它会拦截请求,返回该资源的拷贝,而不会去源服务器重新下载。
大致分为两类: 私有缓存
与共享缓存
。MDN上主要介绍了浏览器与代理缓存,除此之外还有网关缓存
、cdn缓存
、反向代理缓存
、负载均衡器
等部署在服务器上的缓存方式。
HTTP/1.1
定义的 Cache-Control
头用来区分对缓存机制的支持情况, 请求头和响应头都支持这个属性。通过它提供的不同的值来定义缓存策略。
不得存储
任何关于客户端请求和服务端响应的内容。每次由客户端发起的请求都会下载完整的响应内容。
Cache-Control: no-store
Cache-Control: no-cache
Cache-Control: private
Cache-Control: public
max-age=<seconds>
,表示资源能被缓存的最大时间,相较于Expires
, max-age
是距离请求发起的时间的秒数
,针对应用中那些不会改变的文件,通常可以手动设置一定的时长来保证缓存有效,例如图片,css,js等静态资源。
Cache-Control: max-age=31223122
Cache-Control: must-revalidate
Pragma
是HTTP/1.0标准
中定义的一个header属性,请求中包含Pragma的效果跟在头信息中定义Cache-Control: no-cache相同。但是HTTP的响应头没有明确定义这个属性,所以它不能拿来完全替代HTTP/1.1中定义的Cache-control头。通常定义Pragma以向后兼容基于HTTP/1.0的客户端。
由于缓存只有有限的空间用于存储资源副本,所以缓存会定期地将一些副本删除,这个过程叫做缓存驱逐
。
另一方面,当服务器上面的资源进行了更新,那么缓存中的对应资源也应该被更新,由于HTTP是C/S模式的协议,服务器更新一个资源时,不可能直接通知客户端更新缓存,所以双方必须为该资源约定一个过期时间,在该过期时间之前,该资源(缓存副本)就是新鲜的,当过了过期时间后,该资源(缓存副本)则变为陈旧的。驱逐算法用于将陈旧的资源(缓存副本)替换为新鲜的。
一个陈旧的资源(缓存副本)是不会直接被清除或忽略的,当客户端发起一个请求时,缓存检索到已有一个对应的陈旧资源(缓存副本),则缓存会先将此请求附加一个If-None-Match
头,然后发给目标服务器,以此来检查该资源副本是否是依然还是算新鲜的,若服务器返回了 304 (Not Modified)
(该响应不会有带有实体信息),则表示此资源副本是新鲜的,这样一来,可以节省一些带宽。若服务器通过 If-None-Match 或 If-Modified-Since判断后发现已过期,那么会带有该资源的实体内容返回。
对于含有特定头信息的请求,会去计算缓存寿命。比如Cache-control: max-age=N
的头,相应的缓存的寿命就是N。
通常情况下,对于不含这个属性的请求则会去查看是否包含Expires属性
,通过比较Expires的值和头里面Date属性的值来判断是否缓存还有效。
如果max-age和expires属性都没有,找找头里的Last-Modified
信息。如果有,缓存的寿命就等于头里面Date的值减去Last-Modified的值/10。
// 缓存失效时间计算公式:
// 其中,responseTime表示浏览器接收到此相应的那个时间点。
expirationTime = responseTime + freshnessLifeTime - currentAge
为了优化缓存,过期时间设置得尽量长是一种很好的策略。对于定期或者频繁更新的资源,这么做是比较稳妥的,但是对于那些长期不更新的资源会有点问题。比如网页上引入的一些js/css文件,当它们变动时需要尽快更新线上资源。
web开发者发明了一种叫revving
的技术。不频繁更新的文件会使用特定的命名方式:在URL后面(通常是文件名后面)会加上版本号。加上版本号后的资源就被视作一个完全新的独立的资源,同时拥有一年甚至更长的缓存过期时长
。
但是这么做也存在一个弊端,所有引用这个资源的地方都需要更新链接。web开发者们通常会采用自动化构建工具在实际工作中完成这些琐碎的工作。当低频更新的资源(js/css)变动了,只用在高频变动的资源文件(html)里做入口的改动。
参考资料: 1.MDN Web Docs HTTP缓存
组内分享http相关问题: