内容简介:最近看了不少关于客户端缓存机制的文章,大概弄明白了整个原理,但是对于中间一些细节一直有点迷糊,下面的内容是自己做的验证
最近看了不少关于客户端缓存机制的文章,大概弄明白了整个原理,但是对于中间一些细节一直有点迷糊,下面的内容是自己做的验证
二. 准备工作
http缓存的基本情况可以看看下这些文章
常见的影响缓存的配置有以下几个:
-
原服务器的
Expires
-
原服务器的
Cache-Control
-
代理服务器(nginx)
Expires
-
代理服务器(nginx)
proxy_cache_valid
-
代理服务器(nginx)
proxy_cache_path
中的inactive
当前这篇会先验证无代理服务器的情况,有代理的情况放在之后验证
配置说明
原服务器的 Cache-Control
:
名称 | 说明 |
---|---|
private | 客户端可以缓存,代理服务器不可以缓存 |
public | 客户端和代理服务器都可以缓存 |
max-age=t | 缓存内容将在t秒后失效 |
no-cache | 使用协商缓 |
no-store | 不使用缓存 |
原服务器的 Expires
:
这是http 1.0的属性,现在应该用的少了;该属性设置的是一个过期时间,过期时间内命中强缓存;过期时间外,协商缓存
三. 验证
以下是我准备验证的问题
-
原服务器的
Cache-Control
不同属性的实际情况 -
原服务器的
Expires
的实际情况 -
不使用代理服务器的情况下,原服务器的
Expires
和Cache-Control
同时存在,那个优先级高
1. 首先是无代理服务器的情况
验证方式是使用node的express启动一个服务
const fs = require('fs'); const path = require('path'); const express = require('express'); const app = express(); const port = 3030; app.use(express.static(path.resolve(__dirname, './'))); app.get('/index', (req, res) => { const html = fs.readFileSync(path.resolve(__dirname, './index.html'), 'utf-8'); res.send(html) }) // cache验证 app.all('*', (req, res, next) => { // res.header('Cache-Control', 'private'); // res.header('Cache-Control', 'public'); res.header('Cache-Control', 'max-age=20'); // res.header('Cache-Control', 'no-cache'); // res.header('Cache-Control', 'no-store'); next(); }) const questions = [ { id: '000', name: 'Rose' }, { id: '111', name: 'Jack' } ] app.get('/api/data', (req, res) => { res.status(200); res.json(questions); }) // 监听端口 app.listen(port, () => { console.log(`success listen at ${port}`); }) 复制代码
1.1 Cache-Control的各属性验证
ps:因为不存在代理服务器,所以public和private的区别现在是看不出来的,我们之后和有代理的情况一起验证
// 单独验证Cache-Control app.all('*', (req, res, next) => { res.header('Cache-Control', 'max-age=30'); // res.header('Cache-Control', 'no-cache'); // res.header('Cache-Control', 'no-store'); next(); }) 复制代码
以下验证可以得出结论:
- max-age未过期 -> 命中强缓存
- max-age过期 -> 资源未修改 -> 命中协商缓存
- max-age过期 -> 资源已修改 -> 服务器获取资源
- no-cache -> 资源未修改 -> 命中协商缓存
- no-cache -> 资源已修改 -> 服务器获取资源
- no-store -> 服务器获取资源
Cache-Control | 请求延迟时间 | 资源是否改变 | If-None-Match(request) | ETag(respanse) | Status Code | 结论 |
---|---|---|---|---|---|---|
max-age=30 | 10s | 是 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 200 OK (from disk cache) | 强缓存未过期,命中强缓存 |
max-age=30 | 60s | 是 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo" | 200 OK | 强缓存过期,资源被修改,重新从服务器获取资源 |
max-age=30 | 10s | 否 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 200 OK (from disk cache) | 强缓存未过期,命中强缓存 |
max-age=30 | 60s | 否 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 304 Not Modified | 强缓存过期,资源未修改,命中协商缓存 |
no-cache | 30s | 是 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo" | 200 OK | 资源被修改,未命中协商缓存,从服务器获取资源 |
no-cache | 30s | 否 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 304 Not Modified | 资源未修改,命中协商缓存 |
no-store | 30s | 是 | 无 | W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo" | 200 OK | 不使用缓存,直接从服务端获取资源 |
no-store | 30s | 否 | 无 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 200 OK | 不使用缓存,直接从服务端获取资源 |
1.2 Expires验证
ps1:如果这个 Expires
字段后端没有处理过的话,返回的应该是GMT的标准时间 Wed, 23 Jan 2019 09:52:55 GMT
,也就是格林威治的标准时间;而我们一般使用的是本地时间 Wed Jan 23 2019 18:06:15 GMT+0800 (中国标准时间)
,所以要做一下适当的处理
ps2:我是在chrome上用localhost上测试的,但不知道为什么设置 Expires
后,不管过没过期,不管有没有同时设置 Cache-Control
, Status Code
状态一直是304的,听说好像是因为用了localhost的关系,这个与线上并不一定完全一致;这里关于 Expires
的测试我是加了nginx代理了之后的结果,不过代理的缓存没有设置
ps3: Expires
的结果就仅供参考吧,以上
const moment = require('moment'); // cache验证 app.all('*', (req, res, next) => { res.header('Expires', getGLNZ()); next(); }) // 转换格林威治时间 function getGLNZ() { return moment().utc().add(30, 's').format('ddd, DD MMM YYYY HH:mm:ss') + ' GMT'; } 复制代码
以下验证可以得出结论:
- Expires未过期 -> 命中强缓存
- Expires过期 -> 资源未修改 -> 命中协商缓存
- Expires过期 -> 资源已修改 -> 服务器获取资源
Expires | 请求延迟时间 | 资源是否改变 | If-None-Match(request) | ETag(respanse) | Status Code | 结论 |
---|---|---|---|---|---|---|
当前时间+30s | 10s | 否 | 无 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 200 OK (from disk cache) | 强缓存未过期,命中强缓存 |
当前时间+30s | 60s | 否 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 304 Not Modified | 强缓存过期,资源未修改,命中协商缓存 |
当前时间+30s | 10s | 是 | 无 | W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo" | 200 OK (from disk cache) | 强缓存未过期,命中强缓存 |
当前时间+30s | 60s | 是 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo" | 200 OK | 强缓存过期,资源被修改,重新从服务器获取资源 |
1.3 Expires与Cache-Control优先级
结论: Cache-Control
优先级比 Expires
优先级高
const moment = require('moment'); // cache验证 app.all('*', (req, res, next) => { res.header('Cache-Control', 'max-age=60'); // res.header('Cache-Control', 'no-cache'); // res.header('Cache-Control', 'no-store'); res.header('Expires', getGLNZ()); next(); }) function getGLNZ(){ return moment().utc().add(30, 's').format('ddd, DD MMM YYYY HH:mm:ss') + ' GMT'; } 复制代码
以下验证可以看出,在 Cache-Control
与 Expires
同时设置的情况下, Expires
是失效的
Expires | Cache-Control | 请求延迟时间 | 资源是否改变 | If-None-Match(request) | ETag(respanse) | Status Code | 结论 |
---|---|---|---|---|---|---|---|
当前时间+30s | max-age=60 | 10s | 否 | 无 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 200 OK (from disk cache) | Expires与Cache-Control都未过期,命中强缓存 |
当前时间+30s | max-age=60 | 40s | 否 | 无 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 200 OK (from disk cache) | Expires过期,Cache-Control未过期,命中强缓存 |
当前时间+30s | max-age=60 | 100s | 否 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 304 Not Modified | Expires与Cache-Control都过期,资源未修改,命中协商缓存 |
当前时间+30s | no-cache | 10s | 否 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 304 Not Modified | Expires未过期,但是命中协商缓存 |
当前时间+30s | no-cache | 60s | 否 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 304 Not Modified | Expires过期,命中协商缓存 |
当前时间+30s | no-store | 10s | 否 | 无 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 200 OK | Expires未过期,直接获取服务端资源 |
当前时间+30s | no-store | 60s | 否 | 无 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 200 OK | Expires过期,直接获取服务端资源 |
四. 总结:
1. 只设置 Cache-Control
- max-age未过期 -> 命中强缓存
- max-age过期 -> 资源未修改 -> 命中协商缓存
- max-age过期 -> 资源已修改 -> 服务器获取资源
- no-cache -> 资源未修改 -> 命中协商缓存
- no-cache -> 资源已修改 -> 服务器获取资源
- no-store -> 服务器获取资源
2. 只设置 Expires
- Expires未过期 -> 命中强缓存
- Expires过期 -> 资源未修改 -> 命中协商缓存
- Expires过期 -> 资源已修改 -> 服务器获取资源
3. Cache-Control
与 Expires
同时存在
- 只有Cache-Control生效
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- SpringBoot 缓存实战
- Viewer模型加载本地缓存实战
- SpringBoot 实战 (十一) | 整合数据缓存 Cache
- EVCache缓存在 Spring Boot中的实战
- 原 荐 缓存架构之借助消息中间件RabbitMQ实现Redis缓存实时更新实战演练
- H5 和移动端 WebView 缓存机制解析与实战
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。