缓存 - 一个奇妙的想法

3,375 阅读10分钟

太慢了!

HTTP 你过来一下,有件事想和你商量商量!” 浏览器老大晃悠到了 HTTP 的手术室中说道。

“怎么有空过来,正忙着呢,快坐。” 说着,我放下手中的器具,抽出放在一旁的凳子。

“不了不了,我就想了解了解情况,门口还有还多 URL 等着你发送呢!” 浏览器老大叹了口气。

我一看门外,吓了一跳,门口的 URL 都已经围着我的手术室绕了一圈了。“什么情况?” 我问道。

“这个页面的 HTTP 请求有点多啊!” 浏览器又叹了口气,缓缓的说道:“你想想有什么办法能快一点?”

“这我也没有办法呀,我这只有 6 个人,要不你加点?”

“不行啊,浏览器空间太小了,操作系统老大不愿意在派人过来了,算了,你先忙,我想想办法!” 浏览器老大一脸的无奈。

协商

手术室外的 URL 慢慢的减少,大概过了 500ms 我终于放下了手中的手术刀,长舒一口气,发现了还在手术室内来回踱步的浏览器老大。

HTTP 你有没有发现,有一些 URL 你送了好几次!” 浏览器老大若有所思道。

“我没注意诶,不过印象中是有的。” 说完,我陷入了思考。

“那你回忆回忆,他们带回来的数据,一样吗?” 浏览器老大想到了关键点。

“我想想哈!” 我陷入了回忆中,喃喃自语道:“有一个 URL 来了 3 次,确实有,结果的话,是一样的!”

“真的?那其实都没必要发啊,直接拿上一次的数据给我就行啦!” 庄严的浏览器突然兴奋的像个小孩子。

“是没错,但是...” 我刚想开口。

“没有但是!先这么试试,把之前的数据给存一存,磁盘内存空间你自己调配哈。” 一脸兴奋的浏览器突然打断我的话,跳着就出去了。

“这个老大有点精神大条啊,不行啊,这怎么行啊。” 我自言自语道:“不过想法是个好想法,就是怎么存,哪些资源需要存的问题。首先针对于数据的请求,我应该不用缓存,也就是 Content-type 里有关于 json 的那些请求。其次也不能把文件一直存着吧,要是变了咋办呢!那就先给个固定时间吧!不错,哈哈!我真是个天才!!”

屏幕外的愤怒

“我靠,这 Chrome,我每次发版都要好久才有反应,Google 上说这个是固定缓存时间!都什么嘛!还让不让程序员干活了!”

“加个时间戳吧,所有问题迎刃而解!”

“哎,也太不智能了啊,这可是 Chrome 啊!”

浏览器老大听到略带愤怒的声音 Cue 到了他,不禁一哆嗦,赶忙来到了 HTTP 的手术室!

HTTP 你怎么搞的,现在屏幕外都抱怨起来了,我都听到大家准备用 FireFox 了!” 浏览器老大急的满头汗。

“还不是上次你说的把数据给存起来,我还优化了下,至少没有把 json 数据给存下来,要不然...” 突然间我想到了 FireFox 一脸得意的样子,也急了起来。

“怎么办!要不咱们不优化吧!” 浏览器老大说道。

“其实吧,这几天下来,我也思考了下,文件的缓存不应该由我们来决定,你想他们(屏幕外的开发者)在发布的时候会先把文件发到服务器上,如果服务器可以通知我们更新的话,那就最好不过了!”

“说的有道理,但是有一点,服务器不可能通知到我们啊,因为连接是单向的。” 突然间兴奋又失落,浏览器老大叹了口气:“看来还是不行啊!”

“不不不!这件事我要和我在服务器的哥们谈谈,老大你先别沮丧,我有个挺好的办法的,我先验证验证。如果验证通过了,那么我有信心做好!”

“哦...” 浏览器老大还在失望的情绪里,看来对我的想法不是很相信。

强缓存

拿起办公桌上的电话,转动几圈后,听筒里传来了 “嘟嘟” 声。

“喂,你个不识相的,被派到 Chrome 就了不起了?这么久才来一个电话!”

“太忙了啊!说正事,我有个想法想和你谈谈!”

“什么想法?我这也很忙啊,别说有点没的啊,老娘忙着呢!”

“这个想法一担实现,我们的工作量会减轻不少呢!” 我顿了顿接着说道:“你有没有发觉,其实有很多请求都是一样的?”

“是啊,而且有时候还挺多的!说到这,我也想问,你们客户端的 HTTP 怎么老是请求一样的东西?一直请求一直爽?”

“这个没的办法啊,用户老是刷新页面,还有有的用户每天访问的就那几个页面,这就导致了我们经常向你要同一些东西。”

“那你们缓存下来啊!别动不动就向我要!”

“我们已经开始试验了,但实验效果一直不理想,一开始我们设定的缓存时间是一天,但是页面的变动我们完全不知道!”

“是哦,这些天从你哪儿的请求确实少了不少。”

“现在浏览器老大因为缓存不更新数据这个问题,已经打算放弃缓存了!但我有个想法,可以解决这个问题,需要试一试才行!”

“什么想法,说吧,我来配合你!”

“还记得报文头吗?”

“当然,我们不就干这个的吗?”

“你可以给个资源的有效时间吗?”

“有效时间?什么意思?”

“就是资源不会发生变动的时长。”

“这个我也不知道咯!”

“让开发者设置啊,把这个权限给他们,我们约定这个有效时间放在报文头中行吧!”

“嗯,是个不错的想法,然后你们就根据这个设置,缓存对应的资源?”

bingo ~”

OK 那我向我这边的应用层程序开放这个设置,报文头中我给你加个 Expires 的字段,代表过期时间,你直接用就好!”

“这么贴心!谢啦!”

“好嘞!那我先去通知一下!”

屏幕外的自责

“兄弟,不好意思哈,我刚刚把这个资源设置成了明天过期 ~~ 你强刷一下试试?”

“...” 默默的按下了 CTRL + F5 “兄弟,没用...”

“啊!这个资源中间要走代理服务器...”

“那咋办?”

“加个时间戳?”

“不行啊!”

“会不会你没发成功?”

“...”

协商缓存

“喂?在吗?”

“又有什么事?老娘很忙的!”

“还记得上次我找你吗?我们加了个 Expires 的响应头?”

“嗯,怎么了,不挺好的吗?你们把锅成功摔给了我们!” 电话那头传来了愤怒的声音:“老娘真是着了你的道!这些开发者,明明自己设置了 Expires,还以为没发好,整天的往服务器上传东西,烦死了!”

“消消气 ~ 消消气 ~ 我这还有一个事呢。” 我一脸尴尬的说道:“在改版之后呢,少了不少请求,但是我们发现资源一旦过期了,我们还是得向你们要,但返回的数据我们对比了下,还是一样的!所以这里还是需要和你们配合优化一下。”

“还来!” 电话那头传来了略带愤怒的声音:“还想甩锅?”

“消消气消消气,想想这能减少好多工作量呢!这次也是加一个响应头就好 ~”

“说吧!”

“你们那边能根据资源生成一个唯一标识吗?就是资源变了这个标识也会变的那种。”

“能啊,这还不简单,根据修改时间就好了啊!”

“原来这么简单啊,确实修改时间符合这个条件,那你把这个时间给我们,我们存下来,然后请求相同资源的时候我们会把这个标识给你们,你们对比一下,如果一样就直接返回 304 我们就直接使用缓存了,行吧?是不是少了好多工作量?”

“确实!这个想法不错!能减不少的工作量。行吧,给你们加上!就叫 Last-Modified 吧,你们的请求头里的就叫 If-Modified-Since 吧,我去通知应用层程序!”

“得嘞 ~”

翻新

“喂!在吗?”

“哟,大忙人开始闲了?” 对面传来了略带嫉妒的声音。

“对啊,自从开始使用我们上次约定好的报文头,工作量巨减啊,HTTP 协议升级了,听说了吗?” 我有点小得意。

“是啊,升 1.1 了,终于能够长连了,但是效果不是很理想啊,该堵住的地方还是堵住了。” 听起来服务器那边碰到了点难事。

“不管这个,还记得 Expires 这个首部吗?”

“记得,就是过期时间嘛。”

“现在这个描述不准确,我们本地时间和你给的不太一致,我们客户端想要的其实是一个缓存的策略,比如缓存多久时间啊,如何缓存之类的,趁着协议升级,我们也改改吧。”

“嗯,不错,每次都要重新计算过期时间也是挺麻烦的,那这个就交给你们了,我这边直接设置缓存策略。就叫 Cache-Control 吧!根据你们的使用情况,来定规则,这个就交给你了。”

“嗯,好的。我们这边总结一下,等下传真给你。”

“关于缓存,还有一个事,我也要提一下。” 电话那头,语气略带抱歉。

“嗯嗯,你说。”

“其实根据修改时间来确定缓存资源不是很准确,我们这的规则是只要文件被打开了就认为是修改了,所以有误差,还有就是如果文件的修改时间小于 1 秒,那个这个值其实是不变的!”

“还有这样的问题啊,那有解决办法吗?”

“我们这可以根据文章内容生成标识符,我通过新的报文头 ETag 给你们,我们这已经准备支持了。这次准备提到新的协议规范里。”

OK 这不错,那我们使用哪个字段给你们?”

If-None-Match

“好的,我记一下。”

URL 来了,我先挂了哈 ~ 策略等下给你传真,你收一下!”

“嗯,忙去吧。”

Cache-Control

大约过了 100ms 服务端的传真机开始打印,内容如下:

指令 作用
public 使用者都可以缓存(客户端和代理服务器)。
private 仅客户端可以缓存(比如浏览器)。
max-age = time 仅缓存 time 所规定的时间,一旦缓存过期,需要重新请求。
s-max-age = time 仅在代理服务器中生效,覆盖 max-age 效果。
no-store 不缓存响应。
no-cache 缓存,但缓存立即失效,配合 ETag 使用。
max-stable = time time 时间内,即使缓存失效,也使用缓存。
min-fresh = time 需要在 time 时间内,重新拿去资源。

失效不代表缓存清楚,仅仅代表缓存过期,如果服务器(也就是你)确认缓存有效,就使用缓存。

指令可组合使用,赋上组合使用结构图:

Cache-Control 使用规则

该图可作为开发者文档。

下图为我们这边总结的缓存命中规则,供你们参考:

命中缓存规则

相关阅读