前几天看到一篇关于缓存的文章完全弄懂 Http 缓存机制 - 基于缓存策略三要素分解法,以为颇有意思,因此打算系统学习下Http缓存相关的知识。html
我把缓存分为缓存存储、缓存对比两部分。前端
基本概念node
命中缓存速度对比算法
200 from cache
vs304 Not Modified
浏览器思考:localStorage存储缓存
Pragma : no-cache http1.0时期的属性
为了兼容会使用服务器
Expires:0 http1.0时期的属性
分布式
Cache-Control http1.1
中加入的新属性,它有如下经常使用参数:性能
Public/Private 私有缓存/共有缓存学习
no-cache:不建议使用本地缓存,但仍然会缓存到本地
no-store:不会在客户端缓存任何数据
max-age:比较特殊,是一个混合属性,替代了Expires的过时时间
举个栗子:若是要设置客户端不缓存,并兼容http1.0的方式能够这样写:
Pragma : no-cache Expires:0 Cache-Control:no-store
等价于
Pragma : no-cache // Pragma为了兼容http1.0 Cache-Control:max-age=0 // 去掉了Expires属性(下面名词解释会说到为何被去掉),合并到max-age中,
名词解释:
私有缓存:《HTTP权威指南》里面讲到了私有缓存的一种就是在浏览器里面输入about:cache
能够查看本身浏览器缓存的内容,会给出一个显示了缓存内容“磁盘缓存统计”页面,这个能够看看还挺有意思,能展现很多信息
Expires:过期期限值,GMT格式,是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过时时间前浏览器能够直接从浏览器缓存取数据,而无需再次请求。不过Expires 是HTTP 1.0的东西,如今默认浏览器均默认使用HTTP 1.1,因此它的做用基本忽略。Expires 的一个缺点就是,返回的到期时间是服务器端的时间,这样存在一个问题,若是客户端的时间与服务器的时间相差很大(好比时钟不一样步,或者跨时区),那么偏差就很大,因此在HTTP 1.1版开始,使用Cache-Control: max-age=秒替代。
Last-Modified http1.0时期属性
如今仍在使用
ETag(Entity Tag) http1.1时期新加属性
,使用inode+mtime(如下有解释)来计算。根据实体内容生成的一段hash字符串(相似于MD5或者SHA1以后的结果),能够标识资源的状态。 当资源发送改变时,ETag也随之发生变化。
名词解释:
inode :包含文件的元信息,包括如下内容
文件的字节数、文件拥有者的User ID、文件的Group ID
文件的读、写、执行权限
文件的时间戳,共有三个:ctime指inode上一次变更的时间,mtime指文>件内容上一次变更的时间,atime指文件上一次打开的时间。
连接数,即有多少文件名指向这个inode、 文件数据block的位置
mtime:指文件内容上一次变更的时间
某些服务器不能精确获得文件的最后修改时间, 这样就没法经过最后修改时间来判断文件是否更新了。
某些文件的修改很是频繁,在秒如下的时间内进行修改. Last-Modified只能精确到秒。
一些文件的最后修改时间改变了,可是内容并未改变。 咱们不但愿客户端认为这个文件修改了。
ETag也有他本身的问题,因此常常在使用中会关闭ETag。举例来讲,同一个文件在不一样物理机上的inode是不一样的,这就致使了在分布式的Web系统中,当访问落在不一样的物理机上时会返回不一样的ETag,进而致使304失效,降级为200请求。解决办法也有从ETag算法中剥离inode,只是使用mtime,可是这样实际上和Last-Modified就同样了。固然你也能够额外的作一些改进,使ETag对静态资源的算法也是经过hash计算的。不过因为通常咱们会使用CDN技术,所以集群部署中的ETag问题并不会形成太大的影响,因此折腾的人也不太是不少。 参考:这篇文章hefangshi同窗的回答
图片描述
引一张《HTTP权威指南》中的一张图,能够看出命中缓存过程:
缓存命中 > 缓存再验证成功 > 缓存未命中 = 缓存再验证失败;
Cache-Control
http1.1
> Expires > Pragmahttp1.0
来决定是否 (200 from cache)
根据Last-Modified
http1.0
和 ETaghttp1.1
来验证是否返回 (304 Not Modified) 二者都有,就必须同时验证,而且二者都知足才会返回304;
图片描述
服务端响应头 Last-Modified 与 客户端请求头 If-Modified-Since 对应
服务端响应头 ETag 与 客户端请求头 If-None-Match
为何有时候明明命中了缓存,控制台中Status显示的不是 200 from cache
?原来是浏览器的缘由:
触发 200 from cache:
直接点击连接访问
输入网址按回车访问
二维码扫描
触发 304 Not Modified:
刷新页面时触发
设置了长缓存、但 Entity Tags 没有移除时触发
毫无疑问选择能够尽可能多的命中缓存,而后靠更新静态文件的版本号来使缓存失效。关于版本号建议使用 file.xxx.js 的形式而不是 file.js?v=xxx。
能够看这两篇文章有讲述缘由:
在研究缓存问题的时候,知乎上看到这个问题:静态资源(JS/CSS)存储在localStorage有什么缺点?为何没有被普遍应用? ,看了大神们的答案主要是维护成本实在太高,若是真的速度超快,这点能够忽略,值得花时间研究,可是若是读取再执行的速度可能会比浏览器直接304性能要低,就彻底没有必要使用这种方式了。