HTTP协议 (四) 缓存
HTTP协议提供了非常强大的缓存机制, 了解这些缓存机制,对提高网站的性能非常有帮助。 本文介绍浏览器和Web服务器之间如何处理"浏览器缓存",以及控制缓存的http header.
本文会使用Fiddler来查看HTTP request和Response, 如果不熟悉这工具,可以先参考[Fiddler教程] 。在看本文的时候, 请务必打开Fiddler来实践。
阅读目录
- 缓存的概念
- 缓存的好处
- Fiddler可以方便地查看缓存的header
- 如何判断缓存新鲜度
- 通过最后修改时间,判断缓存新鲜度
- 与缓存相关的header
- ETag
- 浏览器不使用缓存
- 直接使用缓存,不去服务器端验证
- 如何设置IE不使用缓存
- 公有缓存和私有缓存的区别
缓存的概念
缓存这个东西真的是无处不在, 有浏览器端的缓存, 有服务器端的缓存,有代理服务器的缓存, 有ASP.NET页面缓存,对象缓存。 数据库也有缓存, 等等。
http中具有缓存功能的是浏览器缓存,以及缓存代理服务器。
http缓存的是指:当Web请求抵达缓存时, 如果本地有“已缓存的”副本,就可以从本地存储设备而不是从原始服务器中提取这个文档。
缓存的好处
缓存的好处是显而易见的, 好处有,
1. 减少了冗余的数据传输,节省了网费。
2. 减少了服务器的负担, 大大提高了网站的性能
3. 加快了客户端加载网页的速度
Fiddler可以方便地查看缓存的header
Fiddler中把header都分门别类的放在一起,这样方便查看。

如何判断缓存新鲜度
Web服务器通过2种方式来判断浏览器缓存是否是最新的。
第一种, 浏览器把缓存文件的最后修改时间通过 header ”If-Modified-Since“来告诉Web服务器。
第二种, 浏览器把缓存文件的ETag, 通过header "If-None-Match", 来告诉Web服务器。
通过最后修改时间, 来判断缓存新鲜度
1. 浏览器客户端想请求一个文档, 首先检查本地缓存,发现存在这个文档的缓存, 获取缓存中文档的最后修改时间,通过: If-Modified-Since, 发送Request给Web服务器。
2. Web服务器收到Request,将服务器的文档修改时间(Last-Modified): 跟request header 中的,If-Modified-Since相比较, 如果时间是一样的, 说明缓存还是最新的, Web服务器将发送304 Not Modified给浏览器客户端, 告诉客户端直接使用缓存里的版本。如下图。

3. 假如该文档已经被更新了。Web服务器将发送该文档的最新版本给浏览器客户端, 如下图。

实例: 打开Fiddler, 然后打开博客园首页。然后F5刷新几次浏览器。 你会看到博客园首页也用了缓存。

与缓存有关的header
我们来看看每个header的具体含义。
Request
| Cache-Control: max-age=0 | 以秒为单位 |
| If-Modified-Since: Mon, 19 Nov 2012 08:38:01 GMT | 缓存文件的最后修改时间。 |
| If-None-Match: "0693f67a67cc1:0" | 缓存文件的Etag值 |
| Cache-Control: no-cache | 不使用缓存 |
| Pragma: no-cache | 不使用缓存 |
Response
| Cache-Control: public | 响应被缓存,并且在多用户间共享, (公有缓存和私有缓存的区别,请看另一节) |
| Cache-Control: private | 响应只能作为私有缓存,不能在用户之间共享 |
| Cache-Control:no-cache | 提醒浏览器要从服务器提取文档进行验证 |
| Cache-Control:no-store | 绝对禁止缓存(用于机密,敏感文件) |
| Cache-Control: max-age=60 | 60秒之后缓存过期(相对时间) |
| Date: Mon, 19 Nov 2012 08:39:00 GMT | 当前response发送的时间 |
| Expires: Mon, 19 Nov 2012 08:40:01 GMT | 缓存过期的时间(绝对时间) |
| Last-Modified: Mon, 19 Nov 2012 08:38:01 GMT | 服务器端文件的最后修改时间 |
| ETag: "20b1add7ec1cd1:0" | 服务器端文件的Etag值 |
如果同时存在cache-control和Expires怎么办呢?
浏览器总是优先使用cache-control,如果没有cache-control才考虑Expires
ETag
ETag是实体标签(Entity Tag)的缩写, 根据实体内容生成的一段hash字符串(类似于MD5或者SHA1之后的结果),可以标识资源的状态。 当资源发送改变时,ETag也随之发生变化。
ETag是Web服务端产生的,然后发给浏览器客户端。浏览器客户端是不用关心Etag是如何产生的。
为什么使用ETag呢? 主要是为了解决Last-Modified 无法解决的一些问题。
1. 某些服务器不能精确得到文件的最后修改时间, 这样就无法通过最后修改时间来判断文件是否更新了。
2. 某些文件的修改非常频繁,在秒以下的时间内进行修改. Last-Modified只能精确到秒。
3. 一些文件的最后修改时间改变了,但是内容并未改变。 我们不希望客户端认为这个文件修改了。
实例, 打开Fiddler, 打开博客园首页。 你可以看到很多图片,或者CSS文件都是用了缓存。 这些都是通过比较ETag的值,来判断文件是否更新了。

浏览器不使用缓存
CTRL+F5强制刷新浏览器,或者设置IE。 可以让浏览器不使用缓存。
1. 浏览器发送Http request, 给Web 服务器, header中带有Cache-Control: no-cache. 明确告诉Web服务器,客户端不使用缓存。
2. Web服务器将把最新的文档发送给浏览器客户端.
实例:
打开Fiddler, 打开博客园首页, 然后按CTRL+F5强制刷新浏览器,你将看到

Pragma: no-cache的作用和Cache-Control: no-cache一模一样。 都是不使用缓存。
Pragma: no-cache 是HTTP 1.0中定义的, 所以为了兼容HTTP 1.0. 所以会同时使用Pragma: no-cache和Cache-Control: no-cache
直接使用缓存,不去服务器验证
按F5刷新浏览器和在地址栏里输入网址然后回车。 这两个行为是不一样的。
按F5刷新浏览器, 浏览器会去Web服务器验证缓存。
如果是在地址栏输入网址然后回车,浏览器会"直接使用有效的缓存", 而不会发http request 去服务器验证缓存,这种情况叫做缓存命中,如下图

实例: 比较第一次访问博客园主页和第二次博客园主页
1. 启动Fiddler, 用firefox打开博客园主页, 发现有50多个session。
2. 按CTRL+X将Fiddler中的所有session删除。 关闭firefox,重新打开一个firefox,打开博客园主页。 发现只有30多个session.
分析; 少了的session是因为firefox直接用了缓存,而没有发http request。

如何设置IE不使用缓存
打开IE。点击工具栏上的, 工具->Internet选项->常规->浏览历史记录 设置. 选择“从不”。然后保存。
然后点击“删除” 把Internet临时文件都删掉 (IE缓存的文件就是Internet临时文件)。

公有缓存和私有缓存的区别
Cache-Control: public 指可以公有缓存, 可以是数千名用户共享的。
Cache-Control: private 指只支持私有缓存, 私有缓存是单个用户专用的。

附: HTTP协议 系列教程, (连载中, 敬请期待)
浏览器缓存机制
2012-11-28 14:03 by 吴秦, 11592 阅读, 33 评论, 收藏, 编辑
浏览器缓存机制
浏览器缓存机制,其实主要就是HTTP协议定义的缓存机制(如: Expires; Cache-control等)。但是也有非HTTP协议定义的缓存机制,如使用HTML Meta 标签,Web开发者可以在HTML页面的<head>节点中加入<meta>标签,代码如下:
|
html code |
|
<META HTTP-EQUIV="Pragma" CONTENT="no-cache"> |
上述代码的作用是告诉浏览器当前页面不被缓存,每次访问都需要去服务器拉取。使用上很简单,但只有部分浏览器可以支持,而且所有缓存代理服务器都不支持,因为代理不解析HTML内容本身。
下面我主要介绍HTTP协议定义的缓存机制。
Expires策略
Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。
下面是宝宝PK项目中,浏览器拉取jquery.js web服务器的响应头:
注:Date头域表示消息发送的时间,时间的描述格式由rfc822定义。例如,Date: Mon,31 Dec 2001 04:25:57GMT。
Web服务器告诉浏览器在2012-11-28 03:30:01这个时间点之前,可以使用缓存文件。发送请求的时间是2012-11-28 03:25:01,即缓存5分钟。
不过Expires 是HTTP 1.0的东西,现在默认浏览器均默认使用HTTP 1.1,所以它的作用基本忽略。
Cache-control策略(重点关注)
Cache-Control与Expires的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只不过Cache-Control的选择更多,设置更细致,如果同时设置的话,其优先级高于Expires。
|
http协议头Cache-Control : |
|
值可以是public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age 各个消息中的指令含义如下:
|
还是上面那个请求,web服务器返回的Cache-Control头的值为max-age=300,即5分钟(和上面的Expires时间一致,这个不是必须的)。
Last-Modified/If-Modified-Since
Last-Modified/If-Modified-Since要配合Cache-Control使用。
l Last-Modified:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间。
l If-Modified-Since:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。
Etag/If-None-Match
Etag/If-None-Match也要配合Cache-Control使用。
l Etag:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器觉得)。Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。
l 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比较难解决的问题:
l Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间
l 如果某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存
l 有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形
Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。
用户行为与缓存
浏览器缓存行为还有用户的行为有关!!!
|
用户操作 |
Expires/Cache-Control |
Last-Modified/Etag |
|
地址栏回车 |
有效 |
有效 |
|
页面链接跳转 |
有效 |
有效 |
|
新开窗口 |
有效 |
有效 |
|
前进、后退 |
有效 |
有效 |
|
F5刷新 |
无效 |
有效 |
|
Ctrl+F5刷新 |
无效 |
无效 |
总结
浏览器第一次请求:
浏览器再次请求时:

posted on 2013-07-25 15:02 TrustNature 阅读(621) 评论(0) 收藏 举报

浙公网安备 33010602011771号