浏览器缓存(Browser Catching)是为了节约网络的资源加速浏览,浏览器在用户磁盘上对最近请求过的文档进行存储,当访问者再次请求这个页面时,浏览器就可以从本地磁盘显示文档,这样就可以加速页面的阅览。 —-摘自《百度百科》
浏览器缓存是提升网页性能的一大利器,但是,也是一把双刃剑。利用的好网页的性能会有大幅度提升,服务器的压力也会减小。利用的不好,也会遇到很多的问题。本文结合浏览器缓存的知识,结合真实案例进行分析,希望对读者有所帮助。
浏览器缓存分类
浏览器缓存分为强缓存和协商缓存,浏览器加载一个页面的简单流程如下:
- 浏览器先根据这个资源的http头信息来判断是否命中强缓存。如果命中则直接加在缓存中的资源,并不会将请求发送到服务器。
- 如果未命中强缓存,则浏览器会将资源加载请求发送到服务器。服务器来判断浏览器本地缓存是否失效。若可以使用,则服务器并不会返回资源信息,浏览器继续从缓存加载资源。
- 如果未命中协商缓存,则服务器会将完整的资源返回给浏览器,浏览器加载新资源,并更新缓存。
强缓存
命中强缓存时,浏览器并不会将请求发送给服务器。在Chrome的开发者工具中看到http的返回码是200,但是在Size列会显示为(from cache)。
强缓存是利用http的返回头中的Expires或者Cache-Control两个字段来控制的,用来表示资源的缓存时间。Expires
该字段会返回一个时间,比如Expires:Thu,31 Dec 2037 23:59:59 GMT。这个时间代表着这个资源的失效时间,也就是说在2037年12月31日23点59分59秒之前都是有效的,即命中缓存。这种方式有一个明显的缺点,由于失效时间是一个绝对时间,所以当客户端本地时间被修改以后,服务器与客户端时间偏差变大以后,就会导致缓存混乱。于是发展出了Cache-Control。
Cache-Control
Cache-Control是一个相对时间,例如Cache-Control:3600,代表着资源的有效期是3600秒。由于是相对时间,并且都是与客户端时间比较,所以服务器与客户端时间偏差也不会导致问题。
Cache-Control与Expires可以在服务端配置同时启用或者启用任意一个,同时启用的时候Cache-Control优先级高。
协商缓存
若未命中强缓存,则浏览器会将请求发送至服务器。服务器根据http头信息中的Last-Modify/If-Modify-Since或Etag/If-None-Match来判断是否命中协商缓存。如果命中,则http返回码为304,浏览器从缓存中加载资源。
Last-Modify/If-Modify-Since
浏览器第一次请求一个资源的时候,服务器返回的header中会加上Last-Modify,Last-modify是一个时间标识该资源的最后修改时间,例如Last-Modify: Thu,31 Dec 2037 23:59:59 GMT。
当浏览器再次请求该资源时,上送的请求头中会包含If-Modify-Since,该值为缓存之前返回的Last-Modify。服务器收到If-Modify-Since后,根据资源的最后修改时间判断是否命中缓存。
如果命中缓存,则返回http304,并且不会返回资源内容,并且不会返回Last-Modify。由于对比的服务端时间,所以客户端与服务端时间差距不会导致问题。但是有时候通过最后修改时间来判断资源是否修改还是不太准确(资源变化了最后修改时间也可以一致)。于是出现了ETag/If-None-Match。
ETag/If-None-Match
与Last-Modify/If-Modify-Since不同的是,Etag/If-None-Match返回的是一个校验码(ETag: entity tag)。ETag可以保证每一个资源是唯一的,资源变化都会导致ETag变化*。服务器根据浏览器上送的If-None-Match值来判断是否命中缓存。
* ETag扩展说明
我们对ETag寄予厚望,希望它对于每一个url生成唯一的值,资源变化时ETag也发生变化。神秘的Etag是如何生成的呢?以Apache为例,ETag生成靠以下几种因子
- 文件的i-node编号,此i-node非彼iNode。是Linux/Unix用来识别文件的编号。是的,识别文件用的不是文件名。使用命令’ls –I’可以看到。
- 文件最后修改时间
- 文件大小
生成Etag的时候,可以使用其中一种或几种因子,使用抗碰撞散列函数来生成。所以,理论上ETag也是会重复的,只是概率小到可以忽略。生产问题分析
背景:某次投产,某系统投产后由于强缓存设置时间不恰当导致变更的功能没有体现。后来通过变更文件路径强行解决问题。
变更上下文根,导致URL变化一定可以解决问题。但我们不可能每一次都这么做;还有,在浏览器端关闭缓存、或者清除缓存后再继续浏览、同时使用Ctrl+F5刷新,也可以解决问题,但是我们也不可能让每一个客户在投产后都做一次这个操作。
那我们怎么办呢?从问题原因来看,是将经常变化的资源缓存时间设置的过长导致的。理论上来讲,只要正确划分经常变化资源与不经常变化资源就可以解决问题。但是谁也不能保证不经常变化的资源就一定不会变化。
万一不经常变化的资源变更了怎么办呢?在资源请求的URL中增加一个参数,比如:css/main.css?v=20160105。这个参数是一个版本号,客户化在js代码中,每一次投产的时候变更一下,当这个参数变化的时候,强缓存都会失效并重新加载。这样一来,即使是不常变化的资源,投产以后也需要重新加载。这样就完美的解决了问题。
转载地址:http://web.jobbole.com/86366/