最近在准备优化日志请求时遇到了一些令人疑惑的问题,比如为什么响应头里出现了两个 cache control、为什么明明设置了 no cache 却还是发请求,为什么多次访问时有时请求里带了 etag,有时又没有带?等等。。。
后来查了一些资料以及同事亲自验证,总算对这些问题有了个清晰的理解,现在整理出来以备忘。
缓存分为服务端侧(server side,比如 Nginx、Apache)和客户端侧(client side,比如 web browser)。
服务端缓存又分为 代理服务器缓存 和 反向代理服务器缓存(也叫网关缓存,比如 Nginx反向代理、Squid等),其实广泛使用的 CDN 也是一种服务端缓存,目的都是让用户的请求走”捷径“,并且都是缓存图片、文件等静态资源。
客户端侧缓存一般指的是浏览器缓存,目的就是加速各种静态资源的访问,想想现在的大型网站,随便一个页面都是一两百个请求,每天 pv 都是亿级别,如果没有缓存,用户体验会急剧下降、同时服务器压力和网络带宽都面临严重的考验。
浏览器缓存控制机制有两种:HTML Meta标签 vs. HTTP头信息
浏览器缓存机制,其实主要就是HTTP协议定义的缓存机制(如: Expires; Cache-control等)。但是也有非HTTP协议定义的缓存机制,如使用HTML Meta 标签,Web开发者可以在HTML页面的
节点中加入标签,代码如下:
1
|
<
META
HTTP-EQUIV
=
"Pragma"
CONTENT
=
"no-cache"
>
|
上述代码的作用是告诉浏览器当前页面不被缓存,每次访问都需要去服务器拉取。使用上很简单,但只有部分浏览器可以支持,而且所有缓存代理服务器都不支持,因为代理不解析HTML内容本身。而广泛应用的还是 HTTP头信息 来控制缓存,下面我主要介绍HTTP协议定义的缓存机制。
1
2
3
4
5
6
7
8
9
|
值可以是public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age
各个消息中的指令含义如下:
Public指示响应可被任何缓存区缓存。
Private指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。
no-cache指示请求或响应消息不能缓存,该选项并不是说可以设置”不缓存“,容易望文生义~
no-store用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存,完全不存下來。
max-age指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。
min-fresh指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
max-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。
|
1
2
|
Last-Modified:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间。
If-Modified-Since:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。
|
1
2
|
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。
|
1
2
3
|
Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间
如果某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存
有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形
|
浏览器缓存行为还有用户的行为有关,如果大家对 强制刷新(Ctrl + F5) 还有印象的话应该能立刻明白我的意思~
用户操作 |
Expires/Cache-Control |
Last-Modified/Etag |
地址栏回车 |
有效 |
有效 |
页面链接跳转 |
有效 |
有效 |
新开窗口 |
有效 |
有效 |
前进、后退 |
有效 |
有效 |
F5刷新 |
无效(BR重置max-age=0) |
有效 |
Ctrl+F5刷新 |
无效(重置CC=no-cache) |
无效(请求头丢弃该选项) |
[1] 浏览器缓存机制
http://www.cnblogs.com/skynet/archive/2012/11/28/2792503.html
[2] Web 开发人员需知的 Web 缓存知识
http://www.oschina.net/news/41397/web-cache-knowledge
[3] 浏览器缓存详解:expires,cache-control,last-modified,etag详细说明
http://blog.csdn.net/eroswang/article/details/8302191
[4] 在浏览器地址栏按回车、F5、Ctrl+F5刷新网页的区别
http://cloudbbs.org/forum.php?mod=viewthread&tid=15790
http://blog.csdn.net/yui/article/details/6584401
[5] Cache Control 與 ETag
https://blog.othree.net/log/2012/12/22/cache-control-and-etag/
[6] 缓存的故事
http://segmentfault.com/blog/animabear/1190000000375344
[7] Google的PageSpeed网站优化理论中提到使用Etag可以减少服务器负担
https://developers.google.com/speed/docs/pss/AddEtags
[8] yahoo的Yslow法则中则提示谨慎设置Etag
http://developer.yahoo.com/performance/rules.html#etags