Monday, May 05, 2008

关于http协议中定义的缓存机制与网站性能

一直以来大家都在用http协议,但是有几个人真正的了解http协议?也许你会说,我只需要会用就可以了, 干吗要去读http协议规范? 某些情况下,我觉得这样说是对的, 甚至绝大多数情况下可能都是对的,在于你是要做什么。 但是, 我还是坚持,在你急急忙忙的完成了一个和http有关的功能之后,还是应该好好想想,你为什么要这样使用http协议,这个时候你应该静下心来好好读读http的协议规范, 作为一个真正的程序员, 你应该这么做。

这里有点搬起石头砸自己脚的意思,因为我自己也只是看了http协议规范中和cache有关的一部分,而且不敢保证是所有相关的部分都阅读过了,所以,下面说的东西仅供参考。

[caching mechanisms]
cache机制是分几个层次的,他们包括服务器端缓存,isp缓存,客户端缓存。
  • 服务端缓存,比如squid,还有本身很多web开发框架支持页面缓存, 这个缓存会影响所有访问这个网站的用户。
  • isp缓存,isp可能缓存网站的内容,这个缓存会影响所有通过这个isp接入的用户,可能所有通过这个isp接入,而且访问某个被缓存网站的用户的请求直接在isp就被返回了,不会到达请求的网站。
  • 客户端缓存,一般指浏览器缓存,这个缓存只影响单个用户, 这样用户直接从本地装载内容,或者只需要发送查询请求(if-modified-since)。
所有这些缓存机制可以极大的提高对用户的响应速度,同时也极大的降低了服务器的负荷,因为大量的请求还没有到达网站服务器就已经被处理了。

[Cache-Control]
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
cache-control是http1.1中定义的头,在http1.1之前(http1.0)是通过expire都来指定cache策略。就是说在http的request/response中指定的这些关于cache策略的头是用来影响前面提到的cache机制, 这些cache机制根据cache头来处理其中的cache内容。
具体的cache-control的写法就不详细说,可以自己参考http规范, 这里就说说几个比较常用的值。
[cache-control: public; max-age=21600]
public/private: public 表示这个响应可以被任何cache机制缓存下来,如果是private表示响应绝对不能被共享缓存(shared cache)cache下来,比如用户的登录进某个网站后看到的用户管理页面,如果这个页面被shared cache缓存下来,假设是被isp缓存下来了,那么所有通过这个isp访问该网页的用户都会得到某个其他用户的管理页面,这是绝对不允许的,这样泄漏了用户隐私信息。
max-age: 单位为秒,表示这个响应可以被缓存多长时间。 如果同时也指定了expire头,那么总是以max-age的值为准。 但是并不总是这样,因为有些缓存机制可能还不支持http1.1规范,那么它们就会忽略cache-control头,而仅仅采用expire头,所以在响应中总是同时指定expire头总是一个比较好的主意.
no-cache:

If the no-cache directive does not specify a field-name, then a cache MUST NOT use the response to satisfy a subsequent request without successful revalidation with the origin server.
就是说no-cache还必须指定可以被缓存的某个/某几个field-name,否则这个响应 不能被缓存下来。
no-store: 表示整个响应不能被保存下来,尤其是一些敏感信息。 请求和响应中都可以指定这个值。

[If-Modified-Since/Last-Modified]
last-modified: 表示请求的网页最后的修改时间(也许说是请求的资源最后修改时间更合适)。只会在响应中携带这个头。一般来说如果请求的是一个文件,这个头可能就是操作系统里面该文件的last-modified时间。
if-modified-since: 如果cache机制缓存了某个响应,这个响应中指定了last-modified头,那么下次有用户请求这个缓存的网页时, cache会向original server(产生这个资源的服务器)发送一个请求,请求中包含if-modified-since头,original server检查请求的资源,并且将服务器上该资源的last-modified和if-modified-since的值进行比较,如果服务器上的资源没有修改,那么返回304(这个响应只包括头),如果服务器上的资源已经有了修改,那么返回200,同时返回新修改过的资源。

如果想要弄清楚http的cache机制,动手实践是必不可少的。 我就是用firefox的一个tamper data的插件访问www.youtube.com来实验的, tamper data可以查看request/response中的头信息。 要说起来,如果做web开发,firefox真是个好东西,它的很多插件都很有意思,还可以推荐一个poster。
[ETag]
ETag 是实现与最近修改数据检查同样的功能的另一种方法:没有变化时不重新下载数据。其工作方式是:服务器发送你所请求的数据的同时,发送某种数据的 hash (在 ETag 头信息中给出)。hash 的确定完全取决于服务器。当第二次请求相同的数据时,你需要在 If-None-Match: 头信息中包含 ETag hash,如果数据没有改变,服务器将返回 304 状态代码。与最近修改数据检查相同,服务器仅仅 发送 304 状态代码;第二次将不为你发送相同的数据。在第二次请求时,通过包含 ETag hash,你告诉服务器:如果 hash 仍旧匹配就没有必要重新发送相同的数据,因为你还有上一次访问过的数据。

[压缩 (Compression)]

一个重要的 HTTP 特性是 gzip 压缩。 关于 HTTP web 服务的主题几乎总是会涉及在网络线路上传输的 XML。XML 是文本,而且还是相当冗长的文本,而文本通常可以被很好地压缩。当你通过 HTTP 请求一个资源时,可以告诉服务器,如果它有任何新数据要发送给我时,请以压缩的格式发送。在你的请求中包含 Accept-encoding: gzip 头信息,如果服务器支持压缩,它将返回由 gzip 压缩的数据并且使用 Content-encoding: gzip 头信息标记。




最后还说点和cache机制无关,但是也和网站性能有关的一个小话题。 用户访问一个网站,如何能做方便的到达他/她最关心的页面? 2次点击,4次点击? 似乎真是个小问题,但在我看来,至少涉及两个方面的问题,一个是易用性的问题,决定了用户是否会真正喜欢并且依赖你的站点; 另外一个是网站性能问题, 如果你把4次点击减少到2次点击,是不是意味着你的服务器承受的负荷也减少了一半? 如果你是在架构一个海量用户访问的站点,比如北京奥运的购票网站,那么这些问题绝对不是小问题。就算你在做小网站,这种努力也是很有价值的。

No comments: