本文经过我对大多数网站的http头分析、自己做网站中的一些缓存设置、服务器的一些配置优化,后又阅读了RFC2616HTTP协议相关内容,以做此文。
1、为什么要cache?
这是个经常提到但很多时候又无法突然准确回答的问题。粗略讲,就是要加速和减压。
加速:试想中国的网络,网通电信(就不提铁通之类)开创了互联互通新障碍,我们的用户在不多交钱的时候就得忍受奇慢的跨网速度。而作为互联网运营者,我们希望用户获得最好的用户体验,因此我们就要打破这个障碍。
很多互联网服务商已经用分布cache解决了这个问题,比如sina,把中心服务器的内容分发到各个省市的cache服务器,根据用户所在的网络(ip段)来确定用户拉去最近服务器上的内容,达到访问速度最快。
对于小型网站,页面静态化也是常用的cache方法,因为web服务器处理静态页面速度优于程序脚本,所以也起到了加速的作用,当然,也为了下一个原因-服务器减压。
减压:显而易见,动态脚本的输出速度比一个静态的页面要慢的多(解释、执行、数据库数据读取)。因此,如果我们让用户只是读取一个静态文件,那硬件成本就会很低。大量用户访问静态文件而很少访问动态脚本,如何以节约昂贵的硬件资源。
可以看出,cache还是有二利而无一害的。
2、目前流行的cache模型
可以看到,在数据库、web上,都做了相应的cache。至于生成静态文件,这个技术应该不是一个问题,所以,我在这里主要说一下通用的前端cache。
3、前端cache工作原理(apache mod_cache、squid)
Squid是一个linux下非常流行的代理服务器,当然,这里我们更多的是使用其缓冲(cache)的功能。
Apache mod_cache是从2.0版本开始加入的一个缓存模块,可以作为类似squid的缓存服务,我只尝试过在使用代理模块(mod_proxy)的时候使用,组合作为一个简易版squid,至于其他应用还没有深挖。
这里,首先提几个http协议里的头标志:
Last-Modified 文档最后修改时间,类似于php中的filemtime
Date 文档从服务器端发出的服务器端时间
Expires 文档过期时间
Cache-Control 这个指令我暂时不能用人类的语言表达清楚,大概就是,如果其他域缓存控制有冲突,一切听这个,优先级别比较高。
Pragma 一些特定指令,一般不用,但用的也很多。因为用其他几个参数就能实现目的了。
突然发现语言逻辑出了问题,居然不能表述cache的原理,故底下作图,简单说明。
这样就清楚多了,废话全省了。看不懂者请我吃饭,我给你们当面讲解。
4、实例
现在访问
http://www.sunboyu.cn/wp-content/themes/zen-in-grey-10/images/headerimage.jpg
第一次访问,我们用firebug网络标签查看:200 OK
然后刷新一下再看:304 Not Modified
综合一下其他域,分析为啥会这样
第一次访问:
请求信息
- Host www.sunboyu.cn
- User-Agent Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)
- Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
- Accept-Language zh-cn,zh;q=0.5
- Accept-Encoding gzip,deflate
- Accept-Charset GB2312,utf-8;q=0.7,*;q=0.7
- Keep-Alive 300
- Connection keep-alive
- Referer http://www.sunboyu.cn/
- Cookie __utma=100971139.746333267.1249527244.1253502591.1253523523.65; __utmz=100971139.1249527244.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); wp-settings-1=editor%3Dhtml%26align%3Dcenter%26m0%3Do%26m1%3Dc%26m2%3Dc%26m3%3Dc%26m4%3Do%26m5%3Do%26m6%3Do%26m7%3Dc%26m8%3Do%26hidetb%3D1%26urlbutton%3Durlfile%26imgsize%3Dlarge%26m9%3Do%26m10%3Do; wp-settings-time-1=1252910178; PHPSESSID=a9af3fbfa19442fe549227e1a355ac52; __utmc=100971139
第一次访问,发送基本的请求信息。
反馈信息:
- Date Mon, 21 Sep 2009 09:09:01 GMT
- Server Apache/2.2.11 (FreeBSD) mod_ssl/2.2.11 OpenSSL/0.9.8e DAV/2
- Last-Modified Wed, 22 Jul 2009 08:13:25 GMT
- Etag "797fe8-45a3-46f46f1fb1340"
- Accept-Ranges bytes
- Content-Length 17827
- Keep-Alive timeout=5, max=100
- Connection Keep-Alive
- Content-Type image/jpeg
服务器反馈了该文件的信息,其中包括date:反馈的时间;last-modified:最后修改时间;etag:页面的姆印信息。
第二次访问:
请求信息:
- Host www.sunboyu.cn
- User-Agent Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)
- Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
- Accept-Language zh-cn,zh;q=0.5
- Accept-Encoding gzip,deflate
- Accept-Charset GB2312,utf-8;q=0.7,*;q=0.7
- Keep-Alive 300
- Connection keep-alive
- Referer http://www.sunboyu.cn/
- Cookie __utma=100971139.746333267.1249527244.1253502591.1253523523.65; __utmz=100971139.1249527244.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); wp-settings-1=editor%3Dhtml%26align%3Dcenter%26m0%3Do%26m1%3Dc%26m2%3Dc%26m3%3Dc%26m4%3Do%26m5%3Do%26m6%3Do%26m7%3Dc%26m8%3Do%26hidetb%3D1%26urlbutton%3Durlfile%26imgsize%3Dlarge%26m9%3Do%26m10%3Do; wp-settings-time-1=1252910178; PHPSESSID=a9af3fbfa19442fe549227e1a355ac52; __utmc=100971139
- If-Modified-Since Wed, 22 Jul 2009 08:13:25 GMT
- If-None-Match "797fe8-45a3-46f46f1fb1340"
- Cache-Control max-age=0
由于信息浏览被缓存在浏览器cache中,所以向服务器请求的时候顺便带上了if-modified-sine信息,if-none-match的信息。
反馈信息:
- Date Mon, 21 Sep 2009 09:09:33 GMT
- Server Apache/2.2.11 (FreeBSD) mod_ssl/2.2.11 OpenSSL/0.9.8e DAV/2
- Last-Modified Wed, 22 Jul 2009 08:13:25 GMT
- Etag "797fe8-45a3-46f46f1fb1340"
- Accept-Ranges bytes
- Content-Length 17827
- Content-Type image/jpeg
此次访问,因为etag信息的比较,服务器确定浏览器本地缓存了此文件。并且在浏览器发送的信息 if-modified-since时间后,服务器的文件并没有发生变化,因此,服务器发送304指令使浏览器读取本地缓存,以减少http信息传输量,提高响应速度。
5、进阶(对缓存服务器的控制)
http关于缓存的控制不仅可以控制浏览器,针对代理服务器依然有效。比如我们使用squid进行反向代理服务,我们可以通过调整http头信息而确定用户是否要访问squid的缓存信息,或者不缓存而直接转发服务器的反馈。
假如现在,一台apache的server跑php,主要是cms,由于工期的问题,没工夫做生成静态了,但我们还希望负载不要太大,因此我们在前边加了一台squid服务器,希望能缓存html结果,这样实现项目快速推进。
当然,服务器部署好之后,还需要程序这边做一些配置,就是发送合适的http的头信息,程序做好修改即可。
首先,我们做一个规划:
网站首页希望缓存5分钟,因为不断有新网站发布。
列表页面缓存1小时,因为不断有新的文章发布,但比首页频度还是下降了一些。
文章最终显示页面缓存一天,因为发布要经过多人审核,一般不会修改,但也难免修改一两个字眼。
为了便于演示,我本地架设了apache和squid。当然鄙视一下自己,是用的windows版本,否则就不能玩剑三了。
言归正传,结构如下:
当访问http://127.0.0.1:8888的时候,squid转发请求至http://127.0.0.1:80获取内容,然后反馈给用户,抑或把缓存的内容直接发送给用户。
现在做首页的发布。
首页缓存5分钟,也就是300秒。
从squid访问apache时间算起,过期时间则为 time()+300
用php发送http头 header(”Expires: ” .gmdate(”D, d M Y H:i:s”,time()+300). ” GMT”);
验证:在仿真环境中模拟,我们用httpwatch来查看squid反馈,根据apache和squid的日志来验证缓存是否生效。
写一个文件
- < ?php
- header("Expires: " .gmdate("D, d M Y H:i:s",time()+300). " GMT");
- echo "Content test!!";
- ?>
部署完毕后访问 http://127.0.0.1 看是否顺利输出。然后访问 http://127.0.0.1:8888 看是否可以看到预期内容。
如果能顺利输出,则证明squid apache工作皆正常。
现在用httpwatch查看头信息
第一次访问:
- HTTP/1.0 200 OK
- Date: Wed, 23 Sep 2009 07:55:42 GMT
- Server: Apache/2.2.13 (Win32) PHP/5.2.6
- Accept-Ranges: bytes
- X-Powered-By: PHP/5.2.6
- Expires: Wed, 23 Sep 2009 08:00:42 GMT
- Content-Type: text/html
- X-Cache: MISS from squid.sunboyu.cn
- X-Cache-Lookup: MISS from squid.sunboyu.cn:8888
- Via: 1.0 squid.sunboyu.cn (squid/3.0.STABLE13-BZR)
- Connection: close
第二次访问:
- HTTP/1.0 200 OK
- Date: Wed, 23 Sep 2009 07:55:42 GMT
- Server: Apache/2.2.13 (Win32) PHP/5.2.6
- Accept-Ranges: bytes
- X-Powered-By: PHP/5.2.6
- Expires: Wed, 23 Sep 2009 08:00:42 GMT
- Content-Type: text/html
- Age: 28
- X-Cache: HIT from squid.sunboyu.cn
- X-Cache-Lookup: HIT from squid.sunboyu.cn:8888
- Via: 1.0 squid.sunboyu.cn (squid/3.0.STABLE13-BZR)
- Connection: close
比较两次访问,两次访问时间 07:55:42 相同,过期时间相同 08:00:42,跟之前设置的5分钟相符。第一次X-Cache显示为MISS,因为一次访问,squid缓存中并没有相关信息,所以从apache中获得,第二次缓存中存在缓存信息,直接读取缓存输出,为之“命中”。
根据这个实验,很容易得知第二和第三条要求的做法:
- < ?php
- header("Expires: " .gmdate("D, d M Y H:i:s",time()+3600). " GMT");
- ……
- ?>
- < ?php
- header("Expires: " .gmdate("D, d M Y H:i:s",time()+86400). " GMT");
- ……
- ?>
只需要给页面加上一个过期的http头,即可实现我们的缓存目的。这样做,比生成页面方便多了。当然,如果求seo效果,可以用重写来实现,缓存效果不变。
如何来显示一下缓存后的结果呢?我们可以写一个一般复杂的php脚本,其中运算数据库都使用,直接在apache运行,压力测试一下;然后使用squid访问,访问一次后,关闭apache,然后再压力测试,比较成绩。^_^,因为有了squid,apache跟mysql都不工作了,当然要非常节约资源。
Squid的合理使用,达到了快速网站开发,减轻服务器压力,提高用户影响的目的。
底下分析下squid在分布式加速方面的应用。
分析新浪的一些图片,发现http头中有这样的信息:
- http://i1.sinaimg.cn/blog/temp/1/2008/0306/U2725P503T1D169F1DT20090701101305.jpg
- HTTP/1.0 304 Not Modified
- Date: Fri, 28 Aug 2009 22:09:32 GMT
- Content-Type: image/jpeg
- Expires: Sat, 28 Aug 2010 22:09:32 GMT
- Last-Modified: Wed, 01 Jul 2009 02:13:05 GMT
- Age: 81943
- X-Cache: HIT from zjm-209.sina.com.cn
- Connection: keep-alive
可以发现是命中了zjm-209.sina.com.cn。
Sina在全国各地有好多服务器,其DNS根据用户访问的ip确定用户需要访问哪台服务器最快,则把请求转发到相应服务器,来提高用户访问速度。
大概结构如下:
总结
Cache的确是个好东西,使用好了,做事事半功倍。不过很多时候我们得cache和”非cache”共用。有些内容需要cache,有些页面却要避免cache。当然squid也给出了接口方面控制每个缓存元素。过度的cache也是不好的,在cache之前,对项目进行严谨的分析和大量的测试是我们创建缓存机制的一个前提。