静态资源文件的缓存问题

前一阵被人问到一个问题:

开发人员修改一文件,版本下发后期望用户可以访问到修改的最新文件,而不是被浏览器缓存过的历史文件,请问Http有机制可以保证用户访问到最新的文件吗?如果没有,在考虑性能的前提下,如何设计一种可行方案呢?

相信不少人第一直觉会想到和浏览器缓存有关的一些缓存头,例如:

  • 与请求内容新鲜度有关的:expires,cache-control
  1. expires指定了文档的失效时间,但是前提要求客户端和服务器端的时钟是同步的,不然就不准确了
  2. cache-control头比实际想象的要复杂的多,cache-control:no-cache表明不应使用缓存文件,而应该直接从服务器重新获取,cache-control:max-age=3600表明从服务器将文档传来之时起,可以认为此文档处于新鲜状态的秒数。
  • 与条件请求有关的头,If-Modified-Since,If-None-Match,Last-Modified,Etag。
    浏览器认定文档新鲜度过期后,需要重新请求服务器,此时可以附带一些条件参数,例如文档最近一次修改的时间,文档的实体标记etag值,服务器会拿请求报文中的值与服务器中保存的值进行比较,如果两者一致,表明文档还可以继续使用,此时以304(文档未修改)状态码作为回应,否则将新的内容返回客户端。

我们把问题细化一下,修改的文件存在两种情况:

  1. 该文件的内容是需要动态填充的,这时缓存的策略为不缓存,每次请求都去服务器重新验证

  2. 对于静态文件的修改,举几个例子看看:
    下面这个是github页面上公共图标的缓存情况,cache-control配置了一个很大的失效时间,同时结合last-modified头实施缓存策略。


    github页面上公共图标

下面这个是知乎中个人头像的缓存情况,可以看到采用了cache-control和etag控制缓存


知乎个人头像

现在的问题是:上述图标要是发生了改变,用户浏览器如何才能及时得到更新呢?

因为cache-control配置了一个很大的失效时间间隔,在用户本地存在缓存的情况下,浏览器是不会再次发起请求的

  • 对于github的图标还好理解,因为是网站公共的图标,被更改的频率会很小,在这种背景下,可能在下一次用户请求该网站时,用户浏览器已经不存在此网站的缓存了,所以是可以更新到最新状态的。

  • 对于知乎用户头像的缓存策略,初看起来似乎很矛盾,用户更改头像是随时可能会发生的事情,如何在用户头像更改之后网站内容可以及时更新呢?仔细想想,其实我们的担心是多余的,用户上传新的头像后,系统会给新头像分配新名称,这样在用户重新请求主页面时,动态填充的内容已经发生了变化,服务器会返回新的主页面给浏览器,浏览器解析到了新的用户头像连接,由于在浏览器缓存中并没有找到对应的缓存文件,所以浏览器会针对新的用户头像发起Http请求,进而得到最新的用户头像

图片和样式文件的更改一般不会给网站带来灾难性的影响,但如果是js文件被修改但是用户浏览器依旧使用的是过期的缓存文件,这种情况相比较而言对网站的影响就要大得多。

如何避免此类问题呢?结合知乎个人头像的例子,不难想到的一种方案就是对修改的脚本文件添加一个修改的标志,类似下面这个样子

<script src="dir/test.js?modify=true"></script>

如果频繁修改呢,下面这种方式似乎给好一点
<script src="dir/test.js?version=2.0"></script>

上面的方案都是基于script标签的,在模块化大行其道的今天,脚本加载器应该是会考虑诸如此类实际问题的,例如在seajs中有下面的配置功能

seajs.config({ vars: { 'version': '2' } });
define(function(require, exports, module) {
var lang = require('./dir/test.js?version={version}');
});

考虑一下现实吧,假设文件A在系统中很重要,因此存在大量文件引用,如果还采用上述的方案,这无疑是烦人的体力劳动,如何解脱呢?

总体的方案是:

在动态请求的文件中给静态文件动态添加类似于版本号的标志,然后对服务器配置url重写功能(例如apache服务器),在java中可以配置过滤器,对特定的文件进行url重写。

下面给出stackoverflow上一个基于php的实现方案,原文在这里

+ 首先,在apache的配置文件.htaccess中开启重写功能,并且添加规则
RewriteEngine on RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ $1.$2 [L]

  • 给文件追加mtime标志

function auto_version($file){ if(strpos($file, '/') !== 0 || !file_exists($_SERVER['DOCUMENT_ROOT'] . $file)) return $file; $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $file); return preg_replace('{\\.([ ^./]+)$}', ".$mtime.\$1", $file); }

  • 实际使用

<script href="<?php echo auto_version('/js/base.js'); ?> />

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350

推荐阅读更多精彩内容