blog icon indicating copy to clipboard operation
blog copied to clipboard

奇怪的缓存策略

Open lmk123 opened this issue 9 years ago • 1 comments

今天项目上线之后,有人反馈部分页面出现了问题,但一刷新就好了。我检查了一下,直接从地址栏进入的时候,html 上加载的是 app.hash-a.js,但一旦刷新,html 加载的就是 app.hash-b.js 了。

简而言之,浏览器将 index.html 这个文件缓存下来了,随后在开发者工具的 Network 面板里也看到,直接在浏览器地址栏进入 index.html 时,HTTP 响应是 200(from cache),但一旦刷新,就变成 200(ok) 了。

于是我打开 chrome://cache/,果然在里面找到了我们项目的网址,点进去看到 HTTP 响应头是:

HTTP/1.1 200 OK
Server: Tengine/2.1.2
Date: Thu, 22 Sep 2016 13:47:55 GMT
Last-Modified: Thu, 22 Sep 2016 12:32:00 GMT
Content-Type: text/html
Vary: Accept-Encoding
Content-Encoding: gzip

我并没有在里面看到 Cache-Control,但为什么 Chrome 会将这个网页缓存下来呢?

在谷歌上搜索了几个关键字“no cache-control but chrome cached”,搜到了这个问题:http://webmasters.stackexchange.com/questions/53942/why-is-this-response-being-cached

简单来说,当一个响应里面有 Last-Modified 但没有 Cache-ControlExpires 的时候,浏览器会有自己的一套算法来决定这个文件会被缓存在本地多长时间。

从 Chrome 53 的此处源码(HttpResponseHeaders::GetFreshnessLifetimes)来看,Chrome 的算法是:

(响应头中的 Date - 响应头中的 Last-Modified 的值) * 0.1

所以文章一开始的响应会被 Chrome 缓存大约 7 分钟。在这 7 分钟之内,Chrome 会直接从本地读取缓存,7 分钟之后,Chrome 会请求一次服务器,然后再根据上面的算法缓存一段时间,依此类推。

所以,我们要做的改进就是加上 Cache-Control 响应头,明确的告诉浏览器不要缓存 index.html。

一点题外话

这次这个问题之所以会被发现,是因为后端的一个接口新增了两个必须传的参数,后端上线之后,旧版的 app.js 在请求这个接口的时候没有传这两个新的参数,所以报了错。

这不由得让我想起了另外两个缓存机制:Application Cache 和 Service Worker。这两个缓存机制都是“慢一拍”的:当浏览器检测到缓存更新后,它不会立刻使用新版代码,而是在用户下次访问时才使用新的文件。

所以,后端接口应该做到“向下兼容“——即使使用旧版本的代码也能正常响应,而不是直接抛出一个 400 错误。

lmk123 avatar Sep 22 '16 15:09 lmk123

原来原因是这样哦

cj0x39e avatar Oct 13 '16 07:10 cj0x39e