你值得了解的HTTP缓存机制(代码详解)
Web缓存大致可以分为:数据库缓存、服务器端缓存(代理服务器缓存、CDN缓存)、浏览器缓存。
浏览器缓存也包含很多内容:HTTP缓存、indexDB、cookie、localstorage等等。这里要说的是http缓存。
使用缓存的好处
减少了冗余的数据传输
缓解了网络瓶颈的问题
降低了对原始服务器的要求
降低了距离时延
术语
缓存命中率:从缓存中得到数据的请求数与所有请求数的比率。理想状态是越高越好。
过期内容:超过设置的有效时间,被标记为“陈旧”的内容。通常过期内容不能用于回复客户端的请求,必须重新向源服务器请求新的内容或者验证缓存的内容是否仍然准备。
验证:验证缓存中的过期内容是否仍然有效,验证通过的话刷新过期时间。
失效:失效就是把内容从缓存中移除。当内容发生改变时就必须移除失效的内容。
机制
策略
1)缓存存储策略
缓存存储策略决定了客户端是否应该存储http的response。与缓存存储有关的http header主要为response header中的Cache-Control。该header有下面几个对应的值:Public、Private、no-cache、max-age、no-store。除了no-store,其它几种都会表明response应该被客户端缓存。
指令 | 说明 |
---|---|
Public | 所有内容都将被缓存(客户端和代理服务器都可缓存) |
Private | 内容只缓存到私有缓存中(仅客户端可以缓存,代理服务器不可缓存) |
max-age = xxx (xxx is numeric) | 缓存的内容将在 xxx 秒后失效,失效前可以直接使用本地缓存,失效后必须向服务器确认资源是否已经改变。 |
no-store | 完全不在客户端缓存 |
no-cache | 可以认为等同于 max-age=0 的情况,即将 response 缓存在客户端,但是之后每次都向服务器确认资源是否已经改变 |
通过Cache-Control:Public设置我们可以将HTTP响应数据存储到本地,但此时并不意味着后续浏览器会直接从缓存中读取数据并使用, 因为它无法确定本地缓存的数据是否可用(可能已经失效),需通过缓存过期策略来判断
2)缓存过期策略
缓存过期策略决定了客户端存储在本地的缓存数据是否已过期,如未过期则可以直接使用本地存储的数据,否则就需要发请求到服务端尝试重新获取数据。 与缓存过期策略有关的http header 为Expires。
Expires表示缓存数据有效的绝对时间,告诉客户端到了这个时间点后本地缓存就失效了,在这个时间内客户端可以不请求服务器而直接从本地缓存中使用已存储的结果。
需要注意的是:no-cache和max-age=xxx的优先级高于Expires,当它们同时存在的时候,后者会被覆盖掉。其次, 缓存数据过期只是告诉客户端不能再直接从本地读取缓存了,而是需要再发一次请求到服务器去确认。具体什么情况下本地存储的数据还可以继续使用就与缓存对比策略有关了。
3)缓存对比策略
将缓存在客户端的数据标识发往服务端,服务端通过标识来判断客户端缓存数据是否仍有效,进而决定是否要重发数据。客户端检测到数据过期或浏览器刷新后,会重新发起一个 http 请求到服务器,服务器此时并不急于返回数据,而是看请求头有没有带标识(If-Modified-Since、If-None-Match)过来,如果判断标识仍然有效,则返回304告诉客户端取本地缓存数据来用即可(这里要注意的是你必须要在首次响应时输出相应的头信息(Last-Modified、ETags)到客户端)。 本地缓存数据即使被认为过期,并不等于数据从此就没用了。
缓存过期取值
存储策略里面no-cache等同于max-age=0,假如服务端返回的响应中没有指明max-age、no-cache或Expires时,客户端是否会缓存 http response呢 ?通过Fiddler、Charles等抓包工具可以发现,客户端一样会进行缓存
其取值值为响应头中的Date与Last-Modified之间的差值的10%作为缓存有效时间
在Fiddler的Caching面板中可以看到
HTTP/1.1 Cache-Control Header is present: privateHTTP Last-Modified Header is present: Tue, 08 Nov 2016 06:59:00 GMTNo explicit HTTP Cache Lifetime information was provided.Heuristic expiration policies suggest defaulting to: 10% of the delta between Last-Modified and Date.That's '05:15:02' so this response will heuristically expire 2016/11/11 0:46:01.
用一副图来表示
缓存的控制
1)强制缓存
可以通过Expires,Cache-Control来设定,Expires指缓存过期的时间,超过了这个时间点就代表资源过期。有一个问题是由于使用具体时间,如果时间表示出错或者没有转换到正确的时区都可能造成缓存生命周期出错。并且Expires是HTTP/1.0的标准,现在更倾向于用HTTP/1.1中定义的Cache-Control。两个同时存在时也是Cache-Control的优先级更高。
2)协商缓存
缓存的资源到期了,并不意味着资源内容发生了改变,如果和服务器上的资源没有差异,实际上没有必要再次请求。客户端和服务器端通过某种验证机制验证当前请求资源是否可以使用缓存。 浏览器第一次请求数据之后会将数据和响应头部的缓存标识存储起来。再次请求时会带上存储的头部字段,服务器端验证是否可用。如果返回304 Not Modified,代表资源没有发生改变可以使用缓存的数据,获取新的过期时间。反之返回200就相当于重新请求了一遍资源并替换旧资源。
Last-modified/If-Modified-Since
Last-modified: 服务器端资源的最后修改时间,响应头部会带上这个标识。第一次请求之后,浏览器记录这个时间,再次请求时,请求头部带上If-Modified-Since即为之前记录下的时间。服务器端收到带If-Modified-Since的请求后会去和资源的最后修改时间对比。若修改过就返回最新资源,状态码200,若没有修改过则返回304。
Etag/If-None-Match
由服务器端上生成的一段hash字符串,第一次请求时响应头带上ETag: abcd,之后的请求中带上If-None-Match: abcd,服务器检查ETag,返回304或200。
关于 last-modified 和 Etag 区别
某些服务器不能精确得到资源的最后修改时间,这样就无法通过最后修改时间判断资源是否更新。
Last-modified只能精确到秒。
一些资源的最后修改时间改变了,但是内容没改变,使用Last-modified看不出内容没有改变。
Etag的精度比Last-modified高,属于强验证,要求资源字节级别的一致,优先级高。如果服务器端有提供ETag的话,必须先对ETag进行Conditional Request。
注意:实际使用ETag/Last-modified要注意保持一致性,做负载均衡和反向代理的话可能会出现不一致的情况。计算ETag也是需要占用资源的,如果修改不是过于频繁,看自己的需求用Cache-Control是否可以满足。
实际应用
首先要明确哪些内容适合被缓存哪些不适合。
考虑缓存的内容:css样式文件,js文件,logo、图标,html文件,可以下载的内容一些不应该被缓存的内容:业务敏感的GET请求
可缓存的内容又分为几种不同的情况:
不经常改变的文件:给max-age设置一个较大的值,一般设置max-age=31536000比如引入的一些第三方文件、打包出来的带有hash后缀css、js文件。一般来说文件内容改变了,会更新版本号、hash值,相当于请求另一个文件。 标准中规定max-age的值最大不超过一年,所以设成max-age=31536000。至于过期内容,缓存区会将一段时间没有使用的文件删除掉。
[完]
推荐学习:Html5视频教程