HTTP之缓存
1. 保持副本的新鲜
HTTP 有一些简单的机制可以在不要求服务器记住有哪些缓存拥有其文档副本的情况下,保持已缓存数据与服务器数据之间充分一致。HTTP 将这些简单的机制称为文档过期(document expiration)和服务器再验证(server revalidation)。
1.1 文档过期
通过特殊的 HTTP Cache-Control 首部和 Expires 首部,HTTP 让原始服务器向每个文档附加了一个 "过期日期"。这些首部说明了在多长时间内可以将这些内容视为新鲜的。
在缓存文档过期之前,缓存可以以任意频率使用这些副本,而无需与服务器联系--当然,除非客户端请求中包含有阻止提供已缓存或未验证资源的首部。但一旦已缓存文档过期,缓存就必须与服务器进行核对,询问文档是否被修改过,如果被修改过,就要获取一份新鲜(带有新的过期日期)的副本。
1.2 过期日期和使用期
服务器用 HTTP/1.0+ 的 Expires 首部或 HTTP/1.1 Cache-Control: max-age 响应首部来指定过期日期,同时还会带有响应主体,但由于 Cache-Control 首部使用的是相对时间而不是绝对日期,所以更倾向于使用比较新的 Cache-Control 首部。绝对日期依赖于计算机时钟的正确设置。
过期响应首部
- Cache-Control: max-age:max-age 值定义了文档的最大使用期--从第一次生成文档到文档不再新鲜、无法使用为止,最大的合法生存时间(以秒为单位)。
Cache-Control: max-age=484200
- Expires:指定一个绝对的过期日期。如果过期日期已经过了,就说明文档不再新鲜了。
Expires: Fri, 05 Jul 2002, 05:00:00 GMT
1.3 服务器再验证
仅仅是已缓存文档过期了并不意味着它和原始服务器上目前处于活跃状态的文档有实际的区别;这只是意味着到了要在进行核对的时间了。这种情况被称为 "服务器再验证",说明缓存需要询问原始服务器文档是否发生了变化。
- 如果再验证显示内容发生了变化,缓存会获取一份新的文档副本,并将其存储在旧文档的位置上,然后将文档发送给客户端。
- 如果再验证显示内容没有发生变化,缓存只需要获取新的首部,包括一个新的过期日期,并对缓存中的首部进行更新就行了。
HTTP 协议要求行为正确的缓存返回下列内容之一:
- "足够新鲜" 的已缓存副本;
- 与服务器进行过再验证,确认其仍然新鲜的已缓存副本;
- 如果需要与之进行再验证的原始服务器出故障了,就返回一条错误报文;
- 附有警告信息说明内容可能不正确的已缓存副本。
1.4 用条件方法进行再验证
HTTP 允许缓存向原始服务器发送一个 "条件 GET",请求服务器只有在文档与缓存中现有的副本不同时,才回送对象主体。通过这种方式,将新鲜度检测和对象获取结合成了单个条件 GET。向 GET 请求报文中添加一些特殊的条件首部,就可以发起条件 GET。只有条件为真时,Web 服务器才会返回对象。
HTTP 定义了 5 个条件请求首部。对缓存再验证来说最有用的 2 个首部是 If-Modified-Since 和 If-None-Match。所有的条件首部都以前缀 "If-" 开头。
If-Modified-Since: <date>
:如果从指定日期之后文档被修改过了,就执行请求的方法。可以与 Last-Modified 服务器响应首部配合使用,只有在内容被修改后与已缓存版本有所不同的时候才去获取内容。If-None-Match: <tags>
:服务器可以为文档提供特殊的标签(参见 ETag),而不是将其与最近修改日期相匹配,这些标签就像序列号一样。如果已缓存标签与服务器文档中的标签所有不同,If-None-Match 首部就会执行所请求的方法。
其他条件首部包括 If-Unmodified-Since(在进行部分文件的传输时,获取文件的其余部分之前要确保文件未发生变化,此时这个首部是非常有用的)、If-Range(支持对不完整文档的缓存)和 If-Match(用于与 Web 服务器打交道时的并发控制)。
1.5 If-Modified-Since: date 再验证
最常见的缓存再验证首部是 If-Modified-Since。If-Modified-Since 再验证请求通常被称为 IMS 请求。只有自某个日期之后资源发生了变化的时候,IMS 请求才会指示服务器执行请求:
- 如果自指定日期后,文档被修改了,If-Modified-Since 条件就为真,通常 GET 就会成功执行。携带新首部的新文档会返回给缓存,新首部除了其他信息之外,还包含一个过期日期。
- 如果自指定日期之后,文档没有被修改过,条件就为假,会向客户端返回一个小的 304 Not Modified 响应报文,为了提高有效性,不会返回文档的主体。这些首部是放在响应中返回的,但只会返回那些需要在源端更新的首部。如,Content-Type 首部通常不会被修改,所以通常不需要发送。一般会发送一个新的过期日期。
If-Modified-Since 首部可以与 Last-Modified 服务器响应首部配合工作。原始服务器会将最后的修改日期附加到所提供的文档上去。当缓存要对已缓存文档进行再验证时,就会包含一个 If-Modified-Since 首部,其中携带有最后修改已缓存副本的日期:
If-Modified-Since: <cached last-modified date>
如果在此期间内容被修改了,最后的修改日期就会有所不同,原始服务器就会回送新的文档。否则,服务器会注意到缓存的最后修改日期与服务器文档当前的最后修改日期相符,会返回一个 304 Not Modified 响应。
1.6 If-None-Match: 实体标签再验证
有些情况下仅使用最后修改日期进行再验证是不够的。
- 有些文档可能会被周期性地重写,但实际包含的数据常常是一样的。尽管内容没有变化,但修改日期会发生变化。
- 有些文档可能被修改了,但所做修改并不重要,不需要让世界范围内的缓存都重装数据(比如对拼写或注释的修改)。
- 有些服务器无法准确地判定其页面的最后修改日期。
- 有些服务器提供的文档会在亚秒间隙发生变化,对这些服务器来说,以一秒为粒度的修改日期可能就不够用了。
为了解决这些问题,HTTP 允许用户对被称为实体标签(ETag)的 "版本标识符" 进行比较。实体标签是附加到文档上的任意标签(引用字符串)。它们可能包含文档的序列号或版本名,或者是文档内容的校验和及其他指纹信息。
当发布者对文档进行修改时,可修改文档的实体标签来说明这个新的版本。这样,如果实体标签被修改了,缓存就可以用 If-None-Match 条件首部来 GET 文档的新副本了。
可以在 If-None-Match 首部包含几个实体标签,告诉服务器,带有这些实体标签的对象副本在缓存上已经有了:
If-None-Match: "v2.6"
If-None-Match: "v2.4","v2.5","v2.6"
If-None-Match: "foobar","A34FAC0095","Profiles in Courage"
1.7 强弱验证器
缓存可以用实体标签来判断,与服务器相比,已缓存版本是不是最新的(与使用最近修改日期的方式很像)。从这个角度来看,实体标签和最近修改日期都是缓存验证器(cache validator)。
服务器希望在对文档进行一些非实质性或不重要的修改时,不要使所有的已缓存副本都失效。HTTP/1.1 支持 "弱验证器",如果只对内容进行了少量修改,就允许服务器声明那是 "足够好" 的等价体。
只要内容发生了变化,强验证器就会变化。若验证器允许对一些内容进行修改,但内容的主要含义发生变化时,通常它还是会变化的。有些操作不能用弱验证器来实现(如有条件地获取部分内容),所以,服务器会用前缀 "W/" 来标识弱验证器。
ETag: W/"v2.6"
If-None-Match: W/"v2.6"
不管相关的实体值以何种方式发生了变化,强实体标签都要发生变化。而相关实体在语义上发生了比较重要的变化时,弱实体标签也应该发生变化。
1.8 何时使用实体标签和最近修改日期
如果服务器回送了一个实体标签,HTTP/1.1 客户单就必须使用实体标签验证器。如果服务器只回送了一个 Last-Modified 值,客户端就可以使用 If-Modified-Since 验证。如果实体标签和最后修改日期都提供了,客户端就应该使用这两种再验证方案,这样 HTTP/1.0 和 HTTP/1.1 缓存就可以正确响应了。
如果 HTTP/1.1 缓存或服务器收到的请求既带有 If-Modified-Since,又带有实体标签条件首部,那么只有这两个条件都满足时,才能返回 304 Not Modified 响应。
2. 控制缓存的能力
服务器可以通过 HTTP 定义的几种方式来指定在文档过期之前可以将其缓存多长时间。按照优先级递减的顺序,服务器可以:
- 附加一个 Cache-Control: no-store 首部到响应中去;
- 附加一个 Cache-Control: no-cache 首部到响应中去;
- 附加一个 Cache-Control: must-revalidate 首部到响应中去;
- 附加一个 Cache-Control: max-age 首部到响应中去;
- 附加一个 Expires 日期首部到响应中去;
- 不附加过期信息,让缓存确定确定自己的过期日期。
2.1 no-store 与 no-cache 响应首部
no-store 首部和 no-cache 首部可以防止缓存提供未经证实的已缓存对象:
Pragma: no-cache
Cache-Control: no-store
Cache-Control: no-cache
- 标识为 no-store 的响应会禁止缓存对响应进行复制。缓存通常会像非缓存代理服务器一样,向客户端转发一条 no-store 响应,然后删除对象。
- 标识为 no-cache 的响应实际上是可以存储在本地缓存区中的。只是在与原始服务器进行新鲜度再验证之前,缓存不能将其提供给客户端使用。
HTTP/1.1 中提供 Pragma: no-cache 首部是为了兼容于 HTTP/1.0+。除了与只理解 Pragma: no-cache 的 HTTP/1.0 应用程序进行交互时,HTTP 1.1 应用程序都应该使用 Cache-Control: no-cache。
2.2 max-age 响应首部
Cache-Control: max-age 首部表示的是从服务器将文档传来之时起,可以认为此文档处于新鲜状态的秒数。还有一个 s-maxage 首部,其行为与 max-age 类似,但仅适用于共享(共有)缓存:
Cache-Control: max-age=3600
Cache-Control: s-maxage=3600
服务器可以请求缓存不要缓存文档,或者将最大使用期设置为零,从而在每次访问的时候都进行刷新:
Cache-Control: max-age=0
Cache-Control: s-maxage=0
2.3 Expires 响应首部
不推荐使用 Expires 首部,它指定的是实际的过期日期而不是秒数。
Expires: Fri, 05 Jul 2002, 05:00:00 GMT
2.4 must-revalidate 响应首部
可以配置缓存,使其提供一些陈旧(过期)的对象,以提高性能。如果原始服务器希望缓存严格遵守过期信息,可以在原始响应中附加一个 Cache-Control: must-revalidate 首部。
Cache-Control: must-revalidate
Cache-Control: must-revalidate 响应首部告诉缓存,在事先没有跟原始服务器进行再验证的情况下,不能提供这个对象的陈旧副本。缓存仍然可以随意提供新鲜的副本。如果在缓存进行 must-revalidate 新鲜度检查时,原始服务器不可用,缓存就必须返回一条 504 Gateway Timeout 错误。
2.5 试探性过期
如果响应中没有 Cache-Control: max-age 首部,也没有 Expires 首部,缓存可以计算出一个试探性最大使用期。可以使用任意算法,但如果得到的最大使用期大于 24 小时,就应该向响应首部添加一个 Heuristic Expiration Warning(试探性过期警告,警告13)首部。但很少浏览器会为用户提供这种警告信息。
LM-Factor 算法是一种很常用的试探性过期算法,如果文档中包含了最后修改日期,就可以使用这种算法。LM-Factor 算法将最后修改日期作为依据,来估计文档有多么易变。
2.5 客户端的新鲜度限制
客户端可以用 Cache-Control 请求首部来强化或放松过期时间的限制。
Cache-Control 请求指令
Cache-Control: max-stale
:缓存可以随意提供过期的文件。Cache-Control: max-stale = <s>
:如果指定了参数 `````,在这段时间内,文档就不能过期。这条指令放松了缓存的规则。Cache-Control: min-fresh=<s>
:至少在未来<s>
秒内文档要保持新鲜。这就使缓存规则更加严格了。Cache-Control: max-age = <s>
:缓存无法返回缓存时间长于<s>
秒的文档。这条指令会使缓存规则更加严格,除非同时还发送了 max-stale 指令,在这种情况下,使用期可能会超过其过期时间。Cache-Control: no-cache
:除非资源进行了再验证,否则这个客户端不会接受已缓存的资源。Pragma: no-cache
:同上Cache-Control: no-store
:缓存应该尽快从存储器中删除文档的所有痕迹,因为其中可能会包含敏感信息。Cache-Control: only-if-cached
:只有当缓存中有副本存在时,客户端才会获取一份副本。
Cache-Control 首部的指令
指令 报文类型 描述
----------------------------------------------------------
no-cache 请求 在重新向服务器验证之前,不要返回文档的缓存副本
no-store 请求 不要返回文档的缓存副本,不要保存服务器的响应
max-age 请求 缓存中的文档不能超过指定的使用期
max-stale 请求 文档允许过期(根据服务器提供的过期信息计算),但不能超过指令中
指定的过期值
min-fresh 请求 文档的使用期不能小于这个指定的时间与它的单签存活时间之和。换句话
说,响应必须至少在指定的这段时间之内保持新鲜
no-transform 请求 文档在发送之前不允许被转换
only-if-cached 请求 只有当文档在缓存中才发送,不要联系原始服务器
public 响应 响应可以被任何服务器缓存
private 响应 响应可以被缓存,但只能被单个客户端访问
no-cache 响应 如果该指令伴随一个首部列表的话,那么内容可以被缓存并提供给客户端,
但必须先删除所列出的首部。如果没有指定首部,缓存中的副本在没有重新
向服务器验证之前不能提供给客户端
no-store 响应 响应不允许被缓存
no-transform 响应 响应在提供给客户端之前不能做任何形式的修改
must-revalidate 响应 响应在提供给客户端之前必须重新向服务器验证
proxy-revalidate 响应 共享的缓存在提供给客户端之前必须重新向原始服务器验证。私有的缓存可以
忽略这条指令
max-age 响应 指定文档可以被缓存的时间以及新鲜度的最长时间
s-mas-age 响应 指定文档作为共享缓存时的最长使用时间(如果有 max-age 指令的话,以本指
令为准)。私有的缓存可以忽略本指令
HTTP之缓存的更多相关文章
- 探究javascript对象和数组的异同,及函数变量缓存技巧
javascript中最经典也最受非议的一句话就是:javascript中一切皆是对象.这篇重点要提到的,就是任何jser都不陌生的Object和Array. 有段时间曾经很诧异,到底两种数据类型用来 ...
- 哪种缓存效果高?开源一个简单的缓存组件j2cache
背景 现在的web系统已经越来越多的应用缓存技术,而且缓存技术确实是能实足的增强系统性能的.我在项目中也开始接触一些缓存的需求. 开始简单的就用jvm(java托管内存)来做缓存,这样对于单个应用服务 ...
- ASP.NET Core 中间件之压缩、缓存
前言 今天给大家介绍一下在 ASP.NET Core 日常开发中用的比较多的两个中间件,它们都是出自于微软的 ASP.NET 团队,他们分别是 Microsoft.AspNetCore.Respons ...
- ASP.NET Core 折腾笔记二:自己写个完整的Cache缓存类来支持.NET Core
背景: 1:.NET Core 已经没System.Web,也木有了HttpRuntime.Cache,因此,该空间下Cache也木有了. 2:.NET Core 有新的Memory Cache提供, ...
- [Java 缓存] Java Cache之 DCache的简单应用.
前言 上次总结了下本地缓存Guava Cache的简单应用, 这次来继续说下项目中使用的DCache的简单使用. 这里分为几部分进行总结, 1)DCache介绍; 2)DCache配置及使用; 3)使 ...
- [原创]mybatis中整合ehcache缓存框架的使用
mybatis整合ehcache缓存框架的使用 mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓 ...
- 探索ASP.NET MVC5系列之~~~5.缓存篇(页面缓存+二级缓存)
其实任何资料里面的任何知识点都无所谓,都是不重要的,重要的是学习方法,自行摸索的过程(不妥之处欢迎指正) 汇总:http://www.cnblogs.com/dunitian/p/4822808.ht ...
- 深究标准IO的缓存
前言 在最近看了APUE的标准IO部分之后感觉对标准IO的缓存太模糊,没有搞明白,APUE中关于缓存的部分一笔带过,没有深究缓存的实现原理,这样一本被吹上天的书为什么不讲透彻呢?今天早上爬起来赶紧找了 ...
- 缓存工厂之Redis缓存
这几天没有按照计划分享技术博文,主要是去医院了,这里一想到在医院经历的种种,我真的有话要说:医院里的医务人员曾经被吹捧为美丽+和蔼+可亲的天使,在经受5天左右相互接触后不得不让感慨:遇见的有些人员在挂 ...
- .net 分布式架构之分布式缓存中间件
开源git地址: http://git.oschina.net/chejiangyi/XXF.BaseService.DistributedCache 分布式缓存中间件 方便实现缓存的分布式,集群, ...
随机推荐
- 基于【 MySql 】一 || 主从复制
一.centos7安装mysql 1. 先检查系统是否装有mysql rpm -qa | grep mysql 2. 下载mysql的repo源 wget http://repo.mysql.com/ ...
- 关于js异步的一些知识点
1,什么是单线程,和异步有什么关系 单线程-只有一个线程,只能做一件事 单线程的原因:避免DOM 渲染的冲突 浏览器需要渲染DOM JS 可以修改DOM 结构 JS 执行的时候,浏览器DOM 渲染会暂 ...
- 数据库入门(mySQL):创建数据库
基于JetBrains DataGrip创建数据库.SQL语句创建数据库 MySQL数据库存储引擎和数据类型 创建数据库表及基本操作 导出数据库.删除数据库.导入数据库 一.基于JetBrains D ...
- vue 的虚拟 DOM 有什么好处?
vue 中的虚拟DOM有什么好处?快! 首先了解浏览器显示网页经历的5个过程 1.解析标签,生成元素树(DOM树) 2.解析样式,生成样式树 3.生成元素与样式的关系 4.生成元素的显示坐标 5.显示 ...
- 虚拟机mysql报错的问题
Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (111)解决方法 登陆mysql的时 ...
- (15)while循环
循环结构 : while 循环结构的特点:减少代码的冗余,提高代码的效率注意:只要是循环一定要有判断条件退出循环,不然就成了死循环,程序会一直在内存执行,直到内存耗尽,暴毙..... 语法形式: wh ...
- python3 基础二——基本的数据类型二
一.数字(Number) 1.Python支持三种不同的数值类型:整型(int),浮点型(float),复数(complex) 2.Python数字数据类型用于存储数值 3.数据类型是不允许改变的,这 ...
- nodejs常用框架使用样例
Koa const Koa = require('koa'); const router = require('koa-router')(); const app = new Koa(); const ...
- Python+request 登录接口reponse的返回值token跨py文件的传递《二》
主要使用场景: 一般我们在进行接口测试时,依赖登录接口后reponse中的某些返回值,因此需要将login接口单独写一个py文件,另外的py文件均可调用login的reponse返回值使用.共用登录接 ...
- 使用IntelliJ IDEA配置Tomcat
一.下载Tomcat 1.进入官网http://tomcat.apache.org/,选择download,下载所需Tomcat版本. 此处我们选择下载最新版本Tomcat 9. 注意有zip和exe ...