开发出高性能的网站,第二部分 — 最佳缓冲控制 - 2005-04-02 作者 Thomas A. Powell 和 Joe Lima 第二部分 — 最佳缓冲控制本文的第一部分 (二月份)介绍了如何通过优化代码来尽可能少的传输数据,在本文的第二部分中,我们将着重介绍如何利用Web端的缓冲技术(caching)来尽可能降低传输的频繁度。一旦您开始注意进行有效的缓冲设置,您便可以极大地减少网页加载的次数,尤其对于经常访问您网站的常客和忠诚的访问者来说更是如此,而且还可以降低您整体带宽的消耗,并减少您有限的服务器资源的占用。 Web缓冲的种类 缓冲技术的原理很简单。为防止每次都重复地加载同一内容而带来的各种资源消耗和浪费,我们保留一份内容的本地副本,并且只要它还有效就可以反复使用它。最常见的网页缓冲是浏览器缓冲,浏览器缓冲在最终用户的本地硬盘上储存了图像和其他网页对象的副本,以供重复使用。除此之外,还有别的缓冲,比如web服务器上的,还有网络路径上的,甚至最终用户本地的网络上都有—不过,这些不同的缓冲其目的是基本一致的。从本地浏览器的缓冲上开始,往外有本地网络的代理(proxy)缓冲,这样如果代理缓冲中有相同的内容,本地网络中其它的用户便不必跑到远端的web服务器上去索取内容了,他们直接使用代理缓冲里这些相同的东西就可以了。再往外,你的ISP和再往外的各个ISP们可能都有一个类似的代理缓冲。最后,在web服务器上还会使用一个叫做‘reverse proxy cache’的缓冲来保持住最后生成的网页,准备提交,这样可以减轻服务器重复生成和提交被请求的内容的负担。 我们可以把上述各种网页缓存分成两类:私有的(private)和公共的(public)。私有缓冲,基本上就是浏览器缓冲,对于一个单个的用户代理(user agent)来说是独一无二的,也就是说只有拥有它的某个最终用户才可以使用。其他的缓冲,比如proxy和reverse proxy缓冲都是公共缓冲。它们是共享资源,其中存储的东西可以被不止一个最终用户使用。图一展示了网页主要常见的缓冲类型: 图一:网页中的缓冲 上述图示说明了我们讨论的一个关键问题:网页缓冲存在于网站和网页服务的各个地方,它们尽可能的保存您要访问的网站内容。一方面,从网站性能的角度来讲,我们要让缓冲能够自行发挥作用;另一方面对其能够进行有目的的控制也很关键,比如我们必须可以规定哪些对象可以放入缓冲中、哪些不可以,以及对象保留多长时间等。 新鲜程度和有效性 为了更好的使用缓冲,包括更有效的使用浏览器缓冲,我们需要对某一个资源的有效性提供指示,以表明这个资源是否还可以放在缓冲中。更具体的说,我们需要对网页对象提供一系列的缓冲规则,比如设置失效期,达到失效期的对象就不能再保存在缓冲中了。幸运的是,对于依据缓冲控制规则失效的东西,我们有一些工具来处理它们。 有两个概念控制着缓冲如何工作:新鲜程度(freshness)和验证(validation)。新鲜程度指的是某个放在缓冲的对象是否是新的,换成更技术化的术语来说,就是这个在缓冲中的对象的状态是否和其相对应的在web服务器上的资源的状态一致。如果浏览器缓冲或者其他的缓冲没有足够的信息表明其是新鲜的,则系统就会产生警告错误并把这个对象当作过期的或者是不新鲜的(stale)。验证则是某个缓冲检查原始服务器来确认是否有潜在不新鲜的对象的过程。如果服务器确认某个缓冲对象仍是新鲜的,则浏览器继续使用本地的资源,否则服务器就会送出一份新鲜的拷贝。 一个简单的缓冲例子 我们举一个例子来说新鲜和验证的概念。在此我们使用浏览器缓冲为例,别的公共缓冲的核心原理和浏览器缓冲是一样的。 第一步:远程站点有一个网页叫做page1.html。这个网页引用了image1.gif、image2.gif和image3.gif,并且有一个到page2.html的链接。当我们第一次访问这个网页的时候,HTML和相关的GIF图像就会被一个接一个的被下载到本地的浏览器缓冲中 图二:第一次缓冲加载 一旦数据被下载到缓冲中,这些数据就会被加上‘邮戳’,这个邮戳就标示了它们从哪里来、何时被访问过。邮戳中也有可能包含有第三条信息:何时需要被重新下载。但是,大多数的网站都不会给它们的数据加上这第三条信息,所以我们也假设我们的例子中也不涉及到这第三条信息。 第二步:用户点击了page2.html的链接,这个网页以往从未被访问过,这个网页引用了image1.gif、image3.gif和image4.gif。此时,浏览器下载了这个网页的标示代码,但问题来了:还需要重新下载image1.gif和image3.gif吗,既然它们已经存在于缓冲中了?这个问题的答案显然是不必重新下载。不过,我们如何保证这两个图像自打上次存到缓冲中后,从来没有修改过呢?如果没有缓冲控制信息,可能我们没法保证。所以,浏览器会给服务器发送请求来能够重新验证这些图像是否被修改过。如果没有被修改过,服务器就会快速返回一个304 图三:检查缓冲 从这个简单的例子中可以很明显地看出来,,即使当CSS、图像、和JavaScript是新鲜的,我们也不一定能够享受到缓冲带来的好处,因为浏览器访问一个对象前都要先到服务器那里兜个圈子。 IE 浏览器中缺省的设置是‘自动’,这个设置可以在单次浏览器会话中跳过对缓冲对象再验证的过程,这在一定程度上减少了浏览器和服务器之间的持续的来回通信。您可能注意过在同一次的浏览会话中,一般来说重复访问同一个网页的加载速度都比较快。您如果在浏览器设置中选择‘每次访问网页’这个选项,则您就会发现性能上的下降,这是很多个304 图四:IE的缓冲控制对话框 注意:尽管IE的‘智能缓冲’可以有效地减少不必要的验证请求,它也是IE不断提醒用户若要看到新内容请清除缓冲的罪魁祸首。所以,使用缓冲,必须折中考虑很多事情。 设置缓冲控制策略 通过最小化来回通信的次数可以对浏览器加载次数产生巨大的变化。这种变化最显著的情况出现在下述情况下:经过了第一次访问后,用户再次光顾这个网页。这种情况下,所有的网页对象都得再验证,每次都会消耗宝贵的时间,更不用说消耗的带宽和服务器资源。另一方面,恰当的使用缓冲控制可以让浏览器直接从缓冲中加载原来加载过的对象,而不必‘长途跋涉’再次造访服务器。添加缓冲控制规则的效果,可以在网页的加载时间上看得出来,即使用户使用的是宽带,也可以感觉得到网页渲染的速度变快了。除了用户确实感觉得到的变化外,web服务器也可以从处理大量的缓冲再验证请求的负担重稍事解脱一些,这样进而可以更好的提供服务。 不过,为了更充分的享受到缓冲带来的好处,程序员需要花些时间精心制作一些缓冲控制策略,来按照网站对象可能的生命周期对其进行分类。这里有一个例子,说的是某简单的电子商务网站的一整套缓冲控制策略:
在这个表中,从缓冲控制的角度来看,共有六类不同类型的对象。既然和公司标识及其他品牌标示有关的东西不太可能变化,这些导航和公司标识的图片就可以看作是基本上永久不变的。CSS和JavaScript文件一般来说也是至少半年才会变化。因为网站内容对于搜索引擎优化和用户体验来说至关重要,因此主要的页眉图像可以设置成变化稍微频繁一些。每月的‘特价商品’图像,当然它的保鲜期也就设置成一个月了。还有一些针对个人的特价商品,它们的图象保存在用户的缓冲中,其保鲜期设置成两周,也就是从首次访问开始算起的过期时限是两周。注意,这个类别被标注成‘私有’,以表示这种缓冲不能存在于共享或代理缓冲中。最后,除上述之外其他所有别的内容,它们的缺省都是不能作为缓冲内容的,依次来保证这些文本和动态的内容每次访问请求都是新鲜送出的。 对于缓冲HTML网页,你必须得小心谨慎,不管它们是不是静态生成的,除非你很清楚其中的玄机,如果你不清楚的话,就最好保证这些网页不要缓冲。如果某用户缓冲了你的HTML网页,你设置了一定长度的过期时间,则在过期之前,无论你的网页做了多少更新,该用户都不会看到这些变化。另一方面,如果你只是对于独立的对象进行缓冲的话,比如图像、Flash文件、JavaScript、和样式表单等,简单的进行重命名就可以替换掉这些缓冲内容。打个比方,您设定了一个策略,想要每年更新您站点的标识文件,但只过了半年的时候,您的公司要做品牌形象改变,并且需要对网站进行相应的修改。幸运的是,如果您没有设定HTML网页要做缓冲,您只消改变标识文件的名称(比如logo.gif变成newlogo.gif)并更改相应HTML文件中的 在慎重的考虑过哪些对象可以或者不可以被存入缓冲中,下一步就是要部署这些策略。 管理缓冲 有三种方法来设置缓冲控制规则:
上述三种方法各有其优缺点,我们简要的来总结一下。 通过 最简单设定缓冲控制的方法就是使用 <meta HTTP-EQUIV="Expires" content="Sun, 31 Oct 2004 23:59:00 GMT" />
在上述例子中,浏览器解析到这个HTML时,会认为这个网页到了2004年10月就会过期,并把这个规则加到浏览器的缓冲中。因为这个网页会通过 当然了,缓冲网页虽然可以带来好处,但如上面所述有时我们并不想缓冲它们,这时您可以把 您可能会想用户电脑时钟可能会和服务器的不一样,您可能因为这个会把过期时间设置成很久很久以前,但在实际中,这基本上不是什么问题,因为缓冲控制参考的就是服务器的日期。 和HTTP1.0和HTTP1.1兼容的浏览器都可以使用带有以前时间的 <meta HTTP-EQUIV="Pragma" content="no-cache" /> <meta HTTP-EQUIV="Cache-Control" content="no-cache" />
这两个标签很简单,它们有一个重大问题—中间的代理缓冲读不到它们,因为中间代理不会去解析HTML数据,所以就要依赖HTTP的header来进行缓冲控制了。因为如此,反正浏览器也要读HTTP headers, 编程的缓冲控制 多数的服务器端的编程环境,如:PHP,ASP,和ColdFusion都可以让你修改HTTP Headers来实现一些特殊的功能。比如,在ASP中,你可以在网页的上面加上一些代码,来调用一些现成的Response对象属性: <%
在此你通过ASP来生成适合HTTP1.0的Expires header和适合HTTP1.1的 Expires: Sat, 14 Feb 2004 20:46:04 GMT
这个机制或类似的其他主流服务器端编程环境下的机制比单单使用 不过,服务器端编程环境也面临着一个无法解决的问题。假设,若生成的ASP文件中调用了几个图像,并且根据你设定的缓冲策略,它们的保鲜期是一年。那么,你将如何通过部署HTTP headers来告诉缓冲,这些图像要保存那么长时间呢?你可以使用服务器端的脚本来返回这些图像,但是这样既复杂又浪费资源。在服务器端本身设置缓冲控制信息,这是较理想的为静态的外部的代码(如CSS、JavaScript和二进制对象等)设置缓冲控制信息的方法。 编程缓冲控制 Apache和Microsoft IIS都提供一系列的缓冲控制机制。麻烦的是,这两种常用的web服务器使用不同的方法来缓冲,并且缓冲控制策略的授权也不完全掌握在对网站资源非常熟悉的开发者手中。 Apache上的缓冲控制 就设定缓冲策略而言,在Apache模块正常安装的情况下,Apache比IIS要容易一些。 Apache的服务器管理员,可以通过在服务器的配置文件中(一般来说是httpd.conf)设定mod_expires值来给不同的对象设定保鲜期。在Apache中, mod_expires的 若您的Apache服务器起初没有mod_expires这一项,我们可以生成它,最好方法就是把它制作成一个共享对象(最简单的方法是使用aspx),之后再在httpd.conf文件中加上这样一行: LoadModule expires_module modules/mod_expires.so
如上述,您可以把您配置的指示直接放在httpd.conf文件中。不过,不少管理员更倾向于在外部配置文件中来使用这些东西,这样可以让配置文件干净利落。在我们的例子中,我们也遵循这样的方法,在httpd.conf中使用Apache的 <IfModule mod_expires.c≶ Include conf/expires.conf
|
|