Http 协议的缓存

9.2   HTTP协议的缓存

浏览网页采用的是HTTP协议实现浏览器和服务器的通讯。HTTP协议中包括了完整的缓存管理规范。

9.2.1   缓存部件

用户通过浏览器来访问Plone服务器的时候,中间可能经过浏览器和缓存服务器,如下结构图所示:

+------------+   +---------------------+   +--------------------+
| Zope/Plone |<--| Squid缓存代理服务器 |<--| 浏览器(FireFox/IE) |<---用户
+------------+   +---------------------+   +--------------------+
                         |                           |
                         V                           V
                   /-------------\            /--------------\
                   | Proxy Cache |            | Brower Cache |
                   \-------------/            \--------------/

这里,浏览器和缓存服务器都能提供缓存功能:

  • 浏览器:一般浏览器都可以进行缓存。浏览器根据接收页面的http消息头中缓存设置信息进行缓存。浏览器一般都有清除缓存的功能。

  • Squid/Apache缓存服务器proxy:缓存服务器,Squid更加专业。缓存服务器位于浏览器和,web服务器之间。缓存服务器根据http部分消息头进行缓存。

    web服务器可发送purge指令到缓存服务器,清除过时的缓存。

    Squid支持多个缓存服务器树状阵列,缓存服务器之间通过ICP协议进行缓存信息交换。如果在zope.conf中开启icp服务器,则Zope可设置为squid的上级缓存服务器,让squid在多个zope服务器之间负载均衡。

9.2.2   观察HTTP消息头

HTTP协议包括消息头和消息体。HTTP缓存消息头可控制网页何时、在哪里缓存。

在Linux下,可用 wget -d ,查看完整的HTTP请求和响应消息头。比如查看访问plone.org首页时的消息头如下:

$ wget -d http://plone.org/

[省略前面输出...] ---request begin--- GET / HTTP/1.0 User-Agent: Wget/1.10.2 Accept: */* Host: plone.org Connection: Keep-Alive

---request end--- HTTP request sent, awaiting response... ---response begin--- HTTP/1.0 200 OK Server: Zope/(Zope 2.8.7-final, python 2.3.5, linux2) ZServer/1.1 Plone/2.1.5 (SVN/UNRELEASED) Date: Mon, 11 Dec 2006 03:40:24 GMT X-Pagecache: HIT Content-Length: 43524 Content-Language: en Expires: Fri, 13 Dec 1996 03:39:58 GMT Vary: Accept-Encoding ETag: ||Plone Default|0|652025|False|||||323835 X-Caching-Rule-Id: plone-containers Cache-Control: max-age=0, s-maxage=0, private, must-revalidate Content-Type: text/html;charset=utf-8 X-Header-Set-Id: cache-in-memory X-Cache: MISS from plone.org X-Cache-Lookup: MISS from plone.org:80 Connection: keep-alive

---response end--- [省略后面的输出...]

在response部分,可看到很多消息头,后面将逐一介绍。

另外,firefox浏览器提供了一个LiveHTTPHeaders插件(http://livehttpheaders.mozdev.org/),使用它可在加载页面的时候,观察完整http消息头。

安装后,从 工具 菜单中打开 Live HTTP headers 窗口,访问某个页面,观察http消息头,如图9.4所示。

img/cache/live-http-headers.png

图 9.4 Firefox插件LiveHTTPHeaders的消息头监视窗口

一般页面包括各种图片、css、flash等,对于外部的门户级别网站,所有这些内容一般都需要进行缓存。可利用LiveHTTPHeader插件,可逐一检查是否有遗漏的元素没有缓存。

9.2.3   标准的缓存消息头

Vary, Etag, Cache-Control, Age是标准的缓存消息头,用于控制内容的缓存策略。Plone提供了对这些消息头的控制方法。如果HTTP消息头中出现这些,表示服务器缓存机制在生效了。

9.2.3.1   Cache-Control消息头

Cache-Control 是HTTP协议中主要的缓存控制参数。在上面访问Plone网站首页的输出中,我们看到:

Cache-Control: max-age=0, s-maxage=0, private, must-revalidate

Cache-Control消息头可包括一组控制变量,具体包括:

  • max-age:这个参数告诉浏览器将页面缓存多长时间,超过这个时间后才再次向服务器发起请求检查页面是否有更新。对于静态的页面,比如图片、CSS、Javascript,一般都不大变更,因此通常我们将存储这些内容的时间设置为较长的时间,这样浏览器会不会向浏览器反复发起请求,也不会去检查是否更新了。
  • s-maxage:这个参数告诉缓存服务器(proxy,如Squid)的缓存页面的时间。如果不单独指定,缓存服务器将使用max-age。对于动态内容(比如文档的查看页面),我们可告诉浏览器很快就过时了(max-age=0),并告诉缓存服务器(Squid)保留内容一段时间(比如,s-maxage=7200)。一旦我们更新文档,我们将告诉Squid清除老的缓存版本。
  • must-revalidate:这告诉浏览器,一旦缓存的内容过期,一定要向服务器询问是否有新版本。
  • proxy-revalidate:proxy上的缓存一旦过期,一定要向服务器询问是否有新版本。
  • no-cache:不做缓存。
  • no-store:数据不在硬盘中临时保存,这对需要保密的内容比较重要。
  • public:告诉缓存服务器, 即便是对于不该缓存的内容也缓存起来,比如当用户已经认证的时候。所有的静态内容(图片、Javascript、CSS等)应该是public的。
  • private:告诉proxy不要缓存,但是浏览器可使用private cache进行缓存。一般登录后的个性化页面是private的。
  • no-transform: 告诉proxy不进行转换,比如告诉手机浏览器不要下载某些图片。
  • pre-check/post-check:微软的特殊扩展,一般需要关闭: pre-check=0, post-check=0

9.2.3.2   条件请求和浏览器缓存

当浏览器首次发起页面请求,HTTP请求如下:

GET /some/page/on/the/site

当服务器响应的时候,会同时发送一些有用的信息,比如上次对象修改的时间和一个ETag标签(可选)。浏览器在后续请求发起时,会利用这些信息。

当再次访问这个网页,浏览器先检查Cache-Control消息头中的max-age参数,看保留的页面是否过期了。如果没有max-age参数,则检查Expire消息头。如果页面没有过期,则直接调出浏览器缓存的页面。如果页面过期了,浏览器会向web服务器发送一个条件GET请求,而不是普通的GET请求。条件GET请求如下:

GET /some/page/on/the/site
If-Modified-Since: [浏览器缓存中页面的上次修改时间]
If-None-Match: [浏览器缓存中页面的ETag时间]

服务器可以有2个选择:它可以象一般的GET请求那样响应,返回一个 Status: 200 (成功)的消息头。它也可以更聪明些,它可以检查一下用户所缓存的时间和Etag标签,看用户浏览器所保存的页面和服务器将要提供的页面是否相同。如果相同,它可发送一个消息头为 Status: 304 (没有更改)的空页面。

这样,服务器不必生成完整的页面,因此服务器负载大大降低;同时由于也不必发送完整的页面,带宽耗用也减少了;而用户,也得到了来自服务器的更快的响应速度。

9.2.3.3   ETag标签

前面关于缓存的讨论,都是基于时间的缓存。在基于时间的缓存中,服务器发送了Last-Modified 、 Expires 和 Cache-Control: max-age 消息头。浏览器在缓存过期的时候,才向服务器发送GET请求,并提供一个 If-Modified-Since 的消息头。这种缓存对于需要针对登录用户个性化的页面是不适合的,因为浏览器无法告诉服务器是在匿名方式访问还是登录后的个性化访问,或者是采用不同用户登录的页面。为了缓存个性化的页面,我们需要更多的信息。

ETag标签实际上是服务器标记页面版本的一个任意字符串,用于确定内容是否最新。浏览器可比较自己的缓存页面的ETag和服务器的最新ETag,如果相同则表示缓存页面是最新的了。

ETag是浏览器用来缓存个性化页面的,其核心是一个ETag的生成器。ETag一般和这些因素有关系:

  1. 内容变更的时间
  2. 用户信息的变化(比如登录与否,或者更换了登录用户,因此需要显示新的个性化页面)

这样ETag标签可以由如下信息组成:

当前登录的用户名 + 分隔号 + 页面的修改时间 + 分隔号 + 所在的整点时间

这样,标记的第一段保证用户登出或者更换的时候,ETag会发生变化;第二段确保内容修改的时候,ETag发生变化;第三段让内容在1小时内失效。

9.2.3.4   Vary

表示需要根据某个参数,为某个地址缓存多个变种。比如多种不同的语言缓存不同的内容,需要设置 Accept-Language 。

阅读更多
个人分类: java
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭