缓存的目的
通常而言,用户访问一个web页面的频率远高于web页面更新的频率,因此多数时候用户从服务器获取的html、js、css以及图片等内容都是相同的。如果每次访问都从服务器获取这些静态内容即降低了页面加载的速度,又占用了多余的服务器带宽,显然是一件很不划算的事,所以http协议设计了一套规范,通过若干个http header的结合形成了一个完整的工作体系。
缓存的三要素
http协议中存在若干与缓存有关的header,从功能上来说,大约可以分为三类:
- 缓存存储策略
- 缓存过期策略
- 缓存对比策略
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缓存在客户端,但是之后每次都向服务器确认资源是否已经改变 |
2. 缓存过期策略
缓存过期策略决定了客户端存储在本地的缓存数据是否已过期,如未过期则可以直接使用本地存储的数据,否则就需要发请求到服务端尝试重新获取数据。
与缓存过期策略有关的http header为Expires
。Expires
表示缓存数据有效的绝对时间,告诉客户端到了这个时间点后本地缓存就失效了,在这个时间内客户端可以不请求服务器而直接从本地缓存中使用已存储的结果。
不过这里就不得不吐槽一下http的设计了,明明可以更加清晰一些,却非要在不同的头部之间弄出功能重合的部分:除了Expires
以外,还有若干方法可以指定response的过期时间,如上面我们提到过的Cache-Control
中的max-age
和no-cache
。max-age=xxx
其实相当于Expires : date(当前时间戳+xxx)
(date(当前时间戳+xxx)表示将当前时间戳+xxx的值转化为日期格式)而Cache-Control:no-cache
和 Cache-Control:max-age=0
(单位是秒)相当。
需要注意的是:no-cache
和max-age=xxx
的优先级高于 Expires
,当它们同时存在的时候,后者会被覆盖掉。其次, 缓存数据过期只是告诉客户端不能再直接从本地读取缓存了,而是需要再发一次请求到服务器去确认。具体什么情况下本地存储的数据还可以继续使用就与缓存对比策略有关了。
3. 缓存对比策略
客户端检测到数据过期后,如果服务器在首次响应时返回了Last-Modified
、ETags
,则客户端会发起一个带标识If-Modified-Since
、If-None-Match
的http请求到服务器,如果服务器根据这两个标识判断响应仍然有效,则返回304告诉客户端取本地缓存数据来用即可。
一点要注意的问题
如果服务端返回的响应中没有指明max-age
、no-cache
或Expires
时,客户端是否会缓存http response呢?通过Charles等抓包工具可以发现,客户端一样会进行缓存。那么缓存的过期时间怎么得到呢?
当http响应中没有指明过期时间时,浏览器会取响应头中的Date 与 Last-Modified 之间的差值的10%作为缓存有效时间。
总结
http缓存机制实际上是我们上面提到的三种策略相互综合的结果,所以遇到和http缓存相关的问题时,首先需要从http报文找到三种策略相关的值,才能进一步分析出问题出现的原因所在。
以上。