动漫引擎

导航

网页的缓存Cache与控制

什么是缓存 Cache?

 

缓存位于客户端与服务器之间, 或者服务器与服务器之间。它决定是否保存所获资源的副本,以及如何使用副本,何时更新副本,这里所说的资源包括页面的HTML, 图片,文件等等。

使用缓存主要有2大理由:

  • 减少响应延迟,让页面显示更快:因为缓存比源服务器离客户端更近, 如果直接从缓存而不是源服务器响应客户的请求,耗时更少,让网站看上去响应更快;
  • 减少网络带宽消耗:当副本被重用时,减少了从源服务器获取资源的次数,从而减少带宽消耗。

缓存如何工作?

所有的缓存都遵循以下基本规则

  1. 保留副本:
    • 如果响应头信息告诉缓存器不要保留缓存;
    • 如果请求信息是需要认证或者安全加密的;
    • 如果在回应中不存在校验参数(ETag或者Last-Modified头信息),缓存服务器会认为缺乏直接的更新度信息,内容将会被认为不可缓存。
  2. 使用副本:只有够新鲜,才可以用。够新鲜的条件:
    • 含有完整的过期时间和寿命控制头信息,并且内容仍在保鲜期内;
    • 浏览器已经使用过缓存副本,并且在一个会话中已经检查过内容的新鲜度;
    • 缓存代理服务器近期内已经使用过缓存副本,并且内容的最后更新时间在上次使用期之前;
    • 够新的副本将直接从缓存中送出,而不会向源服务器发送请求;
  3. 更新副本:
    • 如果缓存的副本已经太旧,缓存服务器将向源服务器发出请求校验请求,用于确定是否可以继续使用当前拷贝继续服务;

总之:新鲜度校验是确定内容是否可用的最重要途径。

 

其中一些规则在协议中有定义(HTTP协议1.0和1.1),另一些规则由缓存的管理员设置(浏览器的用户或者代理服务器的管理员);

 

如何控制缓存1: (在客户端的html文件中)

 缓存的控制一般在服务器端,少数在客户端。下面是在客户端控制的一个实例:

实例1:在客户端用HTML meta标签控制浏览器,要求浏览器不要cache这个页面:

    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">

    <meta http-equiv="Pragma" content="no-cache">

    <meta http-equiv="Expires" content="0">

以上设置是最少的、适合于各种主流浏览器的设置。 建议使用。

 

其中,

* Cache-Control里面的 no-cache是专门针对IE6的, 如果不需要支持IE6,就省略它。

* Pragma 里面的 no-cache是HTTP1.0专门针对旧clients的, 如果不需要支持HTTP1.0,就省略它

* Expires是专门针对HTTP1.0 client和proxies的, 如果不需要支持HTTP1.0,就省略它

 

如果以上3项都不支持,最后,就剩下: (不建议用此, 因为用户实际使用的浏览器版本很杂)

    <meta http-equiv="Cache-Control" content="no-store, must-revalidate">

 

这样,在浏览器的network 记录的Request Headers中可以看到:
Cache-Control:no-cache

 

HTML META标签是写在HTML文件里面的, 使用很简单,但是效率并不高,因为只有几种浏览器会遵循这个标记(那些真正会“读懂”HTML的浏览器),没有一种缓存代理服务器能遵循这个规则(因为它们几乎完全不解析文档中HTML内容);有时会在Web页面中增加:Pragma: no-cache这个META标记,如果要让页面保持刷新,这个标签其实完全没有必要。


如果你的网站托管在ISP机房中,并且机房可能不给你权限去控制HTTP的头信息(如:Expires和Cache-Control);

 

如何控制缓存2: (在服务器端)

HTTP头信息,一般由Web服务器自动生成。但是,根据 你使用的服务,你可以在某种程度上进行控制。他们在HTML代码中是看不见的。HTTP头信息可以让你对浏览器和代理服务器如何处理你的副本进行更多的控制。

HTTP头信息发送在HTML代码之前,只有被浏览器和一些中间缓存能看到,一个典型的HTTP 1.1协议返回的头信息看上去像这样:

HTTP/1.1 200 OK
Date: Fri, 30 Oct 1998 13:19:41 GMT
Server: Apache/1.3.3 (Unix)
Cache-Control: max-age=3600, must-revalidate
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Last-Modified: Mon, 29 Jun 1998 02:28:12 GMT
ETag: "3e86-410-3596fbbc"
Content-Length: 1040
Content-Type: text/html

 

Cache-Control(缓存控制) HTTP头信

HTTP 1.1定义的一组头信息属性:Cache-Control响应头信息,让网站的发布者可以更全面的控制他们的内容,并定位过期时间的限制。有用的 Cache-Control响应头信息包括:

  • max-age=[秒] — 执行缓存被认为是最新的最长时间。类似于过期时间,这个参数是基于请求时间的相对时间间隔,而不是绝对过期时间,[秒]是一个数字,单位是秒:从请求时间开始到过期时间之间的秒数。
  • s-maxage=[秒] — 类似于max-age属性,除了他应用于共享(如:代理服务器)缓存
  • public — 标记认证内容也可以被缓存,一般来说: 经过HTTP认证才能访问的内容,输出是自动不可以缓存的;
  • no-cache — 强制每次请求直接发送给源服务器,而不经过本地缓存版本的校验。这对于需要确认认证应用很有用(可以和public结合使用),或者严格要求使用最新数据的应用(不惜牺牲使用缓存的所有好处);
  • no-store — 强制缓存在任何情况下都不要保留任何副本
  • must-revalidate — 告诉缓存必须遵循所有你给予副本的新鲜度的,HTTP允许缓存在某些特定情况下返回过期数据,指定了这个属性,你高速缓存,你希望严格的遵循你的规则。
  • proxy-revalidate — 和 must-revalidate类似,除了他只对缓存代理服务器起作用

*实例: 在Node Express服务器上设置Cache 


如果etag相同, 未改变,返回: HTTP 304("not modified"), 而不必再返回完整数据

* 用express.static(root, [options])

express.static(cwd + '/public/' + publicFolder, maxAge: MAX_AGE)
app.use(express.static(__dirname + '/public', { maxAge: 31557600000 })); // one year

app.use(serveStatic(path.join(__dirname, 'public'), {
maxAge: '1d',
setHeaders: setCustomCacheControl
}))


注意, maxAge中的大写, 但是send本身并不区分maxage和maxAge
maxAge的取值可以是 ms数值, 或时间字串: '1h','1d', 最大365天, 不能是负值

 

* 在response中设置max-age

res.setHeader('Cache-Control', 'public, max-age=31557600'); // one year

* 考虑request header的要求
if (!res.getHeader('Cache-Control')) {
res.setHeader('Cache-Control', 'public, max-age=' + (maxAge / 1000));

* 对指定的文件类别(.js, .css, .img, .font都设置cache
app.use(function (req, res, next) {
if (req.url.match(/^\/(css|js|img|font)\/.+/)) {
res.setHeader('Cache-Control', 'public, max-age=3600'))
}
next();
});

 

校验参数和校

校验是当副本已经修改后,服务器和缓存之间的通讯机制;使用这个机制:缓存服务器可以避免副本实际上仍然足够新的情况下重复下载整个原件。


* 如果没有任何信息说明保鲜期(Expires或Cache-Control),缓存将不会存储任何副本;

*当一份缓存包含Last-Modified信息(最后修改时间),他基于此信息,通过添加一个If-Modified-Since请求参数,向服务器查询:这个副本从上次查看后是否被修改了。
HTTP 1.1介绍了另外一个校验参数: ETag,这是服务器生成的唯一标识符,每次副本的标签都会变化。由于服务器控制了ETag如何生成,缓存服务器可以通过If-None-Match请求的返回没变则当前副本和原件完全一致。
* 所有的缓存服务器都使用Last-Modified时间来确定副本是否够新,而ETag校验正变得越来越流行;
* 所有新一代的Web服务器都对静态内容(如:文件)自动生成ETag和Last-Modified头信息,而你不必做任何设置。

*服务器对于动态内容(例如:CGI,ASP或数据库生成的网站)并不知道如何生成这些信息;

 

创建利于缓存网站的窍门

除了使用新鲜度信息和校验,你还有很多方法使你的网站缓存友好。

  • 保持URL稳定: 这是缓存的金科玉律,如果你给在不同的页面上,给不同用户或者从不同的站点上提供相同的内容,应该使用相同的URL,这是使你的网站缓存友好最简单,也是 最高效的方法。例如:如果你在页面上使用 "/index.html" 做为引用,那么就一直用这个地址;
  • 使用一个共用的库存放每页都引用的图片和其他页面元素;
  • 对于不经常改变的图片/页面启用缓存,并使用Cache-Control: max-age属性设置一个较长的过期时间;
  • 对于定期更新的内容设置一个缓存服务器可识别的max-age属性或过期时间;
  • 如果数据源(特别是下载文件)变更,修改名称,这样:你可以让其很长时间不过期,并且保证服务的是正确的版本;而链接到下载文件的页面是一个需要设置较短过期时间的页面。
  • 万不得已不要改变文件,否则你会提供一个非常新的Last-Modified日期;例如:当你更新了网站,不要复制整个网站的所有文件,只上传你修改的文件。
  • 只在必要的时候使用Cookie,cookie是非常难被缓存的,而且在大多数情况下是不必要的,如果使用cookie,控制在动态网页上;
  • 减少试用SSL,加密的页面不会被任何共享缓存服务器缓存,只在必要的时候使用,并且在SSL页面上减少图片的使用;
  • 使用可缓存性评估引擎,这对于你实践本文的很多概念都很有帮助;

 

编写利于缓存的脚本

脚本缺省不会返回校验参数(返回Last-Modified或ETag头信息)或其他新鲜度信息(Expires或Cache-Control),有些动态脚本的确是动态内容(每次相应内容都不一样),但是更多(搜索引擎,数据库引擎网站)网站还是能从缓存友好中获益的。
一般说来,如果脚本生成的输出在未来一段时间(几分钟或者几天)都是可重复复制的,那么就是可缓存的。如果脚本输出内容只随URL变化而变化,也是可缓存的;但如果输出会根据cookie,认证信息或者其他外部条件变化,则还是不可缓存的。

  • 最利于缓存的脚本就是将内容改变时导出成静态文件,Web服务器可以将其当作另外一个网页并生成和试用校验参数,让一些都变得更简单,只需要写入文件即可,这样最后修改时间也有了;
  • 另外一个让脚本可缓存的方法是对一段时间内能保持较新的内容设置一个相对寿命的头信息,虽然通过Expires头信息也可以实现,但更容易的是用Cache-Control: max-age属性,它会让首次请求后一段时间内缓存保持新鲜;
  • 如果以上做法你都做不到,你可以让脚本生成一个校验属性,并对 If-Modified-Since 和/或If-None-Match请求作出反应,这些属性可以从解析HTTP头信息得到,并对符合条件的内容返回304 Not Modified(内容未改变),可惜的是,这种做法比不上前2种高效;

其他窍门:

  • 尽量避免使用POST,除非万不得已,POST模式的返回内容不会被大部分缓存服务器保存,如果你发送内容通过URL和查询(通过GET模式)的内容可以缓存下来供以后使用;
  • 不要在URL中加入针对每个用户的识别信息:除非内容是针对每个用户不同的;
  • 不要统计一个用户来自一个地址的所有请求,因为缓存常常是一起工作的;
  • 生成并返回Content-Length头信息,如果方便的话,这个属性让你的脚本在可持续链接模式时:客户端可以通过一个TCP/IP链接同时请求多个副本,而不是为每次请求单独建立链接,这样你的网站相应会快很多;

 

 

检查结果: (在Chrome console的network tab中)

看Response Header的对应参数, (不要看request header)

* Jquery.min.js文件, 缓存
cache-control:public, max-age=31536000, stale-while-revalidate=2592000

expires:Wed, 13 Jun 2018 18:12:07 GMT
last-modified:Tue, 20 Dec 2016 18:17:03 GMT

* js文件: 缓存
Cache-Control:public, max-age=2424193
ETag:W/"a7d1321a284347995f377f640993f618"


* index.html: 不缓存
Cache-Control:public, max-age=0
ETag:W/"c15b-15c3adbb400"

* 字体文件: 不缓存
Cache-Control:public, max-age=0
Content-Type:application/x-font-ttf
ETag:W/"2e05c-15c3b47eb20"

尚未要求缓存的文件:
loading.gif
img/list.png

从cloundary来的c162.png已经缓存

 

More:

http://www.chedong.com/tech/cache_docs.html

https://www.mnot.net/cache_docs/

 

posted on 2017-03-06 23:39  动漫引擎  阅读(452)  评论(0编辑  收藏  举报