1. 缓存的分类
- 缓存分为服务端缓存和客户端缓存
- 服务端缓存又分为代理服务器缓存和反向代理服务器缓存(也叫网关缓存,比如Nginx反向代理等)其实广泛使用的CDN也是一种服务器缓存;
- 客户端的缓存一般指的浏览器缓存,目的就是加速各种静态资源的访问;
2. 客户端的缓存机制
- 浏览器缓存控制机制有两种:HTML Meta标签 vs HTTP头信息
- HTTP的缓存策略
- expires策略:Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。不过Expires 是HTTP 1.0的东西,现在默认浏览器均默认使用HTTP 1.1,所以它的作用基本忽略。Expires 的一个缺点就是,返回的到期时间是服务器端的时间,这样存在一个问题,如果客户端的时间与服务器的时间相差很大(比如时钟不同步,或者跨时区),那么误差就很大,所以在HTTP 1.1版开始,使用Cache-Control: max-age=秒替代。
- Cache-control策略:Cache-Control与Expires的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只不过Cache-Control的选择更多,设置更细致,如果同时设置的话,其优先级高于Expires。
- 值可以是public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age各个消息中的指令含义如下
- public 指示响应可被任何缓存区缓存
- private 指示对于单个用户的整个或部分消息,不能共享缓存;
- no-cache 相当于max-age:0,must-revalidate即资源被缓存, 但是缓存立刻过期, 同时下次访问时强制验证资源有效性
- no-store 请求和响应都不缓存
- max-age:指客户机可以接受生存期不大于指定时间的响应
- min-fresh:指客户机可以接受响应时间小于当前时间加上指定时间的响应
- max-stale:指客户机可以接受超出超时期间的响应消息;
- Last-Modified/If-Modified-Since:Last-Modified/If-Modified-Since要配合Cache-Control使用。
Last-Modified:标示这个响应资源的最后修改时间
-
If-Modified-Since:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。缓存校验字段, 其值为上次响应头的Last-Modified值, 若与请求资源当前的Last-Modified值相同, 那么将返回304状态码的响应, 反之, 将返回200状态码响应.
当max-age 与 max-stale 和 min-fresh 同时使用时, 它们的设置相互之间独立生效, 最为保守的缓存策略总是有效. 这意味着, 如果max-age=10 days, max-stale=2 days, min-fresh=3 days, 那么:
根据max-age的设置, 覆盖原缓存周期, 缓存资源将在4月15日失效(5+10=15);
根据max-stale的设置, 缓存过期后两天依然有效, 此时响应将返回110(Response is stale)状态码, 缓存资源将在4月14日失效(12+2=14);
根据min-fresh的设置, 至少要留有3天的新鲜期, 缓存资源将在4月9日失效(12-3=9);
由于客户端总是采用最保守的缓存策略, 因此, 4月9日后, 对于该资源的请求将重新向服务器发起验证.
- Etag/If-None-Match:Etag/If-None-Match也要配合Cache-Control使用。
- Etag:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器决定)。Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。
- If-None-Match:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对,决定返回200或304。
- 既生Last-Modified何生Etag?你可能会觉得使用Last-Modified已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要Etag(实体标识)呢?HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:
- Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间如果某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形
- Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified与ETag一起使用时,服务器会优先验证ETag。
- yahoo的Yslow法则中则提示谨慎设置Etag:需要注意的是分布式系统里多台机器间文件的last-modified必须保持一致,以免负载均衡到不同机器导致比对失败,Yahoo建议分布式系统尽量关闭掉Etag(每台机器生成的etag都会不一样,因为除了 last-modified、inode 也很难保持一致)。
- 值可以是public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age各个消息中的指令含义如下
3. 几个重要概念的解释
-
vary
“vary”本身是“变化”的意思,而在http报文中更趋于是“vary from”(与。。。不同)的含义,它表示服务端会以什么基准字段来区分、筛选缓存版本。我们先考虑这么一个问题——在服务端有着这么一个地址,如果是IE用户则返回针对IE开发的内容,否则返回另一个主流浏览器版本的内容。这很简单,服务端获取到请求的 User-Agent 字段做处理即可。但是用户请求的是代理服务器而非原服务器,且代理服务器如果直接把缓存的IE版本资源发给了非IE的客户端,这就出问题了。
因此 Vary 便是着手处理该问题的首部字段,我们可以在响应报文加上:
Vary: User-Agent
便能知会代理服务器需要以 User-Agent 这个请求首部字段来区别缓存版本,防止传递给客户端的缓存不正确。Vary 也接受条件组合的形式:
Vary: User-Agent, Accept-Encoding
-
Data和Age
- HTTP并没有提供某种方法来帮用户区分其收到的资源是否命中了代理服务器的缓存,但在客户端我们可以通过计算响应报文中的 Date 和 Age 字段来得到答案。
- Date 理所当然是原服务器发送该资源响应报文的时间(GMT格式),如果你发现 Date 的时间与“当前时间”差别较大,或者连续F5刷新发现 Date 的值都没变化,则说明你当前请求是命中了代理服务器的缓存。上述的“当前时间”自然是相对于原服务器而言的时间,那么如何获悉原服务器的当前时间呢?
- 每次你刷新页面,浏览器都会重新发出这条url的请求,你会发现其 Date 值是不断变化的,这说明该链接没有命中缓存,都是从原服务器返回过来的数据。因此我们可以拿页面上其它静态资源请求回包中的 Date 与其进行对比,若静态资源的 Date 早于原服务端时间,则说明命中了代理服务器缓存。
- 这里的 Age 也是响应报文中的首部字段,它表示该文件在代理服务器中存在的时间(秒),如文件被修改或替换,Age会重新由0开始累计。
4. 强制缓存和协商缓存
- 强制缓存:在缓存数据未失效的情况下,可以直接使用缓存数据
- 协商缓存:缓存过期之后,继续请求该资源,对于现代的浏览器有两种方法:
- ETag值进行比较,与IF-NONE-MATCH进行比较
- Modified_value和If-Modified-Since字段进行比较;
- 以上, ETag优先级比Last-Modified高, 同时存在时, 前者覆盖后者.