浏览器缓存知识归纳

Author Avatar
Pang Jian 3月 05, 2016
总字数:1.8k 预计阅读:6 min
  • 在其它设备中阅读本文章

写在开头:这篇文章是打算分享到单位内部的材料。归纳浏览器缓存也是对一个生产问题的分析总结出来的。这片文章经过脱敏以后发表到个人博客上面吧。

浏览器缓存(Browser Catching)是为了节约网络的资源加速浏览,浏览器在用户磁盘上对最近请求过的文档进行存储,当访问者再次请求这个页面时,浏览器就可以从本地磁盘显示文档,这样就可以加速页面的阅览。 —- 摘自《百度百科》

浏览器缓存是提升网页性能的一大利器,但是,也是一把双刃剑。利用的好网页的性能会有大幅度提升,服务器的压力也会减小。利用的不好,也会遇到很多的问题。本文结合浏览器缓存的知识,结合真实案例进行分析,希望对读者有所帮助。

浏览器缓存分类

浏览器缓存分为强缓存和协商缓存,浏览器加载一个页面的简单流程如下:

  1. 浏览器先根据这个资源的 http 头信息来判断是否命中强缓存。如果命中则直接加在缓存中的资源,并不会将请求发送到服务器。
  2. 如果未命中强缓存,则浏览器会将资源加载请求发送到服务器。服务器来判断浏览器本地缓存是否失效。若可以使用,则服务器并不会返回资源信息,浏览器继续从缓存加载资源。
  3. 如果未命中协商缓存,则服务器会将完整的资源返回给浏览器,浏览器加载新资源,并更新缓存。

    强缓存

    命中强缓存时,浏览器并不会将请求发送给服务器。在 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。
Last-Modify
当浏览器再次请求该资源时,上送的请求头中会包含 If-Modify-Since,该值为缓存之前返回的 Last-Modify。服务器收到 If-Modify-Since 后,根据资源的最后修改时间判断是否命中缓存。
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 扩展说明

我们对 ETag 寄予厚望,希望它对于每一个 url 生成唯一的值,资源变化时 ETag 也发生变化。神秘的 Etag 是如何生成的呢?以 Apache 为例,ETag 生成靠以下几种因子

  1. 文件的 i-node 编号,此 i-node 非彼 iNode。是 Linux/Unix 用来识别文件的编号。是的,识别文件用的不是文件名。使用命令’ls –I’可以看到。
  2. 文件最后修改时间
  3. 文件大小
    生成 Etag 的时候,可以使用其中一种或几种因子,使用抗碰撞散列函数来生成。所以,理论上 ETag 也是会重复的,只是概率小到可以忽略。

    生产问题分析

    背景:某次投产,某系统投产后由于强缓存设置时间不恰当导致变更的功能没有体现。后来通过变更文件路径强行解决问题。
    变更上下文根,导致 URL 变化一定可以解决问题。但我们不可能每一次都这么做;还有,在浏览器端关闭缓存、或者清除缓存后再继续浏览、同时使用 Ctrl+F5 刷新,也可以解决问题,但是我们也不可能让每一个客户在投产后都做一次这个操作。
    那我们怎么办呢?从问题原因来看,是将经常变化的资源缓存时间设置的过长导致的。理论上来讲,只要正确划分经常变化资源与不经常变化资源就可以解决问题。但是谁也不能保证不经常变化的资源就一定不会变化。
    万一不经常变化的资源变更了怎么办呢?在资源请求的 URL 中增加一个参数,比如:css/main.css?v=20160105。这个参数是一个版本号,客户化在 js 代码中,每一次投产的时候变更一下,当这个参数变化的时候,强缓存都会失效并重新加载。这样一来,即使是不常变化的资源,投产以后也需要重新加载。这样就完美的解决了问题。

本文参考数篇技术博客、百度百科、维基百科、Apache 官方说明以及自己的理解完成,如有不正确的地方欢迎斧正。
EOF

Documentation licensed under CC BY-SA 4.0.
本文链接:https://www.pangjian.me/2016/03/05/web-cache-detail/