这是一篇针对网站站长、Web开发者与运营维护人员有关缓存Cache的教程。Web缓存是指存在多个Web服务器和客户端之间的缓存,将对请求的响应保存复制拷贝,比如HTML页面、图片和文件,如果从同样的URL有另外一个请求进来,将首先从Web缓存中获得该URL的响应拷贝,而不是再直接向原始服务器获取。
使用Web缓存有两个理由:
如果你检查一下任何现代浏览器的首选项对话框(如Internet Explorer , Safari或Mozilla) ,你可能会注意到一个“缓存”的设置。这让你设置你的电脑硬盘上的一部分空间来存储你浏览过的页面。浏览器缓存根据非常简单的规则运作。它会进行检查以确保该页面是新鲜,通常一个会话一次(即在浏览器的当前调用中)。
这个缓存是非常有用的,当用户点击“后退”按钮或点击一个链接,这样就能查看他们刚刚看过的页面。另外,如果你在你的网站上使用相同的导航方式,这些页面也会使用浏览器的缓存提供这种服务,从而加速加载。
Web的代理缓存的工作原理和浏览器缓存原理是一样的,但规模更大。代理服务数可以同样的方式服务几百个或数千个用户;大型企业和互联网服务供应商ISP往往将它们设置在他们的防火墙,还是作为独立的代理设备(也称为中介) 。
因为代理缓存不是在客户端或源服务器的一部分,而是在网络上,请求必须以某种方式路由转发到它们上面。一种方法是手工设置浏览器的代理配置,配置到相应的代理服务器;另一种方法是使用拦截。拦截代理会通过基础网络本身将Web请求重定向到他们上面,客户就不必对它们进行手工配置,甚至不知道他们的存在。
代理缓存是一种共享缓存;它们通常有大量的用户,并且就是因为这一点,访问量越大,缓存的效果越好,大幅度减少用户的等待时间和网络流量。这是因为越是访问次数多的页面重复使用的次数越高。
也被称为“反向代理缓存”或“surrogate 代理缓存, ”网关缓存也是中间人,但不是由系统网络管理员出于节省带宽而部署,它们通常是由Webmaster网站站长等自己部署的,这样让自己的网站更具可扩展性,可靠性和性能更好。
请求可以被通过许多方法来路由到网关高速缓存,但通常它是某种形式的负载平衡器。
内容交付网络Content delivery networks (CDN) 通过互联网分发网关缓存,把缓存卖给网站。
本篇主要聚焦浏览器和代理缓存。
Web缓存是互联网上最容易被误解的技术之一。尤其是网站管理者害怕失去对网站的控制权,因为代理缓存可以将他们的用户“隐藏”在自己身后,使得网站管理者很难看到谁在使用该网站。不幸的是,即使网络缓存不存在的,在互联网上也有太多的变量,使得网站管理者无法确切了解用户是如何浏览他们的网站。
另外一个问题是:缓存的内容会失效过时,变成脏数据,本教材会告诉你如何配置你的服务器来控制内容的缓存。不过,缓存失效没有一个通用的算法来解决,只能根据具体情况针对性解决。
网站缓存会让你的网站网页内容加载得更快,减轻你的服务器和网络负载。用户如果感觉你的网站加载很快,也就会更频繁访问,同时。搜索引擎如果发现你的网站更快,将会引导更多用户访问你的网站,这对于网站的SEO是有利的。
许多大型互联网公司在全世界各地花巨资复制它们的内容,就是为了让当地用户更快地访问,缓存也是同样的道理,它们更接近终端用户,而且你不需要支付任何费用。
其实,代理缓存和浏览器缓存无论你喜欢或不喜欢都会被采用,如果你不正确配置你的网站使用缓存,它们会使用默认的缓存策略使用缓存。
Web缓存是如何工作的?
所有缓存都有一系列配置来决定什么时候从缓存中获取页面,一些规则是在协议如HTTP 1.0和1.1中设置,一些是由缓存的管理员设置,或者是浏览器缓存的用户,或者是代理缓存的管理者。通常有下面这些通用的规则:
如果一个响应中没有出现验证器(ETag或Last-Modified头),那它就没有任何明确的刷新信息,它通常会 - 但并不总是 - 被认为不可被缓存的。
总之,新鲜度和验证是缓存内容的工作原理与最重要途径。新鲜页面能够立即从高速缓存中获得,而一个验证表示如果它并没有被改变过,就可以避免发送整个页面的内容。
有几个工具可以用于微调缓存:
HTML产生者能够将一些标签放入Html文档的<HEAD>部分,作为其属性描述,这些meta标签经常用于告诉浏览器该Html文档是否可被缓存或在多长时间后失效。Meta标签易于使用,但是并不非常有效率,这是因为它们只被少数浏览器实现,而代理缓存不会承认,因为代理缓存几乎不会阅读文档中HTML语法,当我们试图使用Pragma: no-cache到meta标签,这不一定会让该文档一直保持新鲜度,也就是每次请求都会从原始服务器获取,原因也是因为代理缓存可能会缓存它,尽管你在浏览器保持刷新,浏览器的请求首先经过代理缓存。
而只有HTTP协议的头部才会让你更有力地控制浏览器缓存和代理缓存,它们都不必打开文档阅读Html,通常这些HTTP协议头部信息是由Web服务器产生,比如Nginx或Tomcat,依据你的服务器,你能在某种程度上控制它们。
Http头部会在Html之前被Web服务器发送,只能被浏览器内部等机器阅读,典型地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
HTML会跟在这些头部信息后面,它们之间使用空行分割。
很多人喜欢在HTTP头部设置Pragma: no-cache以避免缓存,这并不总是有效,HTTP规定并没有任何有关Pragma响应头部的规定,Pragma请求头正在讨论中。
使用HTTP头中Expires进行缓存刷新,它是控制缓存的基本手段,它告诉缓存其缓存的内容在多次时间以后就失效了,在失效以后,缓存会再次检查源服务器看看该页面是否已经被改变。
Expires对于静态图片缓存非常有效,因为图片不总是在改变,你可以设置很长的expire时间。比如在Nginx中设置图片有效期是60天:
location ~ .*\.(gif|mp4|jpg|png|ico|css|swf|js$)(.*) {
expires 60d;
...
}
这样你在Http头部看到Expires时间是60天以后那个精确时间格式。
你也可以在Java等Tomcat中配置一个Filter插件:
<filter>
<filter-name>ExpiresFilter</filter-name>
<filter-class>com.jdon.jivejdon.presentation.filter.ExpiresFilter</filter-class>
<init-param>
<param-name>ExpiresDefault</param-name>
<param-value>access plus 60 minutes</param-value>
</init-param>
<init-param>
<param-name>ExpiresByType image</param-name>
<param-value>access plus 10 days</param-value>
</init-param>
<init-param>
<param-name>ExpiresByType text/css</param-name>
<param-value>access plus 10 days</param-value>
</init-param>
<init-param>
<param-name>ExpiresByType text/javascript</param-name>
<param-value>access plus 10 days</param-value>
</init-param>
</filter>
在ExpiresFilter中对Http头部设置Expires:
response.setDateHeader(HEADER_EXPIRES, expirationDate.getTime());
尽管Expire非常有效,也有其缺点,问题是因为其日期,Web服务器的时钟和缓存位置时间必须同步,如果它们的时间不同,可能导致缓存更新不及时;另外一个问题是设置的过期失效时间如果同时在失效那个时间访问你的服务器,会增加负载和延迟。
HTTP 1.1引入了新的头部缓存控制:Cache-Control,弥补了Expire的缺点,Cache-Control有几个值总结如下:
下面是一个案例:
Cache-Control: max-age=3600, must-revalidate
当Cache-Control和Expires都存在,Cache-Control优先。
通过使用验证器,当缓存与原始服务器通讯时,可以在当地已经存在拷贝的情况下,避免将页面完整下载。验证器非常重要,如果没有任何有效期信息如Expires或Cache-Control,也没有验证器,那么缓存就不会缓存任何内容。
最常见的验证器是文本最后修改的时间,在Last-Modified 中设置,当缓存存储带有Last-Modified 头部的页面时,它能使用它询问服务器自从最后修改时间以后这个文档是否已经被改变过,这是使用If-Modified-Since请求实现的。
HTTP 1.1引入了一个新的验证器,称为ETage,ETage是服务器产生的一种唯一标识,每次页面改变时都会产生,因为服务器控制ETag是如何产生的,当缓存向服务器发出If-None-Match请求时,能同时确认ETage是否匹配,如果匹配,缓存中文档和服务器的文档应该还是相同的。
几乎所有的缓存都使用Last-Modified最后修改时间作为验证器,ETage验证也开始变得流行。
大部分现代Web服务器都会自动产生ETag和Last-Modified头部作为静态内容的验证器,动态内容则需要自己在程序中动手设置。