Loading...

Tag Archive for '%e7%bc%93%e5%ad%98'

    AD: 猛买 | 快递查询 | Jobsdigg | 很棒的男装店

8行代码实现SuperCache

使用wordpress的人应该听过wp-supercache的大名,这是一款wordpress插件,用以为blog上的内容生成静态缓存。这样,即使有再多的访问请求,服务器也只需要处理静态文件。我不知道王小峰有没有用wp-supercache,只看到他在抱怨wp太慢(确实挺慢)。 我在使用wp-supercache过程中发现它非常棒,让人甚至感觉不到缓存的存在。我登录以后,在文章页面上会显示出编辑文章的链接,退出登录打开就是一个普通浏览者。我好奇,它是如何判断访问者身份的呢?昨天终于有机会一探究竟,然后在自己的项目中也实现了一个类似的super_cache。 其实奥妙在于wp-supercache在.htaccess中写入的一条段涉及到rewrite的内容: RewriteCond %{REQUEST_METHOD} !=POST RewriteCond %{QUERY_STRING} !.*s=.* RewriteCond %{QUERY_STRING} !.*p=.* RewriteCond %{QUERY_STRING} !.*attachment_id=.* RewriteCond %{QUERY_STRING} !.*wp-subscription-manager=.* RewriteCond %{HTTP_COOKIE} !^.*(comment_author_|wordpress|wp-postpass_).*$ RewriteCond %{HTTP:Accept-Encoding} gzip RewriteCond %{DOCUMENT_ROOT}/wp-content/cache/supercache/%{HTTP_HOST}/$1/index.html.gz -f RewriteRule ^(.*) /wp-content/cache/supercache/%{HTTP_HOST}/$1/index.html.gz [L] 标红的这一行的作用是,如果cookie中包含comment_author/wordpress/wp-postpass_这样的字符,则不进行rewrite。也就是说,当我登录以后,打开任何一个页面都是没有被缓存过的页面;而如果我退出,相应cookie将被清空,此时如果存放缓存的目录有生成好的缓存文件,则进行rewrite,服务器将静态文件发送给我。 我恍然大悟。大部分人其实是“游客”,所以不会带有导致缓存不工作的cookie,这种缓存方式很适合wordpress这样的cms。于是我考虑把这种缓存方式迁移到我的rails项目中。 rails自带了page cache,  action cache 以及fragment cache(还可进行对象缓存,rails还有sql cache,暂不讨论)。其中page cache就是生成静态文件在服务器上,速度最快;action cache速度其次,因为毕竟把action输出的内容缓存了,消耗的是进入rails、执行前置后置过滤器的时间;fragment cache就是页面中的片段缓存,速度最慢。 但rails的page cache默认会生成文件到/public目录。比如这个地址:http://www.niupu.com/subject/311898/,默认生成的page cache是 /public/subject/311898.html。如果下次再有人访问,会直接把静态文件返回。在这种情况下,虽然也可以通过校验cookie使该请求不用静态文件去响应,但这样有点“逆天而行”的感觉,而且很难操作。如果能把缓存文件放到个别的地方(就像wp-supercache),那就好了。 好在rails提供了足够的灵活性。首先需要更换一个保存缓存文件的目录,在environment.rb的 Rails::Initializer.run 这一block中加入一句: config.action_controller.page_cache_directory = RAILS_ROOT+”/public/cache/” 这就把存放缓存文件的位置,由/public目录改到了/public/cache目录。 然后在相应Controller中加入page [...]

WP-SuperCache后台空白的解决

blog的服务器搬家之后我就尝试了WP-Cache和WP-SuperCache,它们的共同问题是启用插件后,我在后台打开它们的管理界面,只能看到空白,某个函数可能在执行过程中被终止了。 可是WP生成一个页面要查询几十次数据库,这个着实很汗颜。后来我用了Cos-HTML-Cache,但一味地生成HTML,虽然负载低了,灵活性降低很多。今天脑袋里灵光一闪,想自己写一个基于memcached的WP插件(服务器上有memcached)。于是打开WP-SuperCache想看看他的实现,顺手调试了一下。没想到这一下就发现了问题,不出所料,确实是程序莫名终止了。 if ( !wp_cache_check_link() || !wp_cache_verify_config_file() || !wp_cache_verify_cache_dir() ) { echo “<br>Cannot continue… fix previous problems and retry.<br />”; echo “</div>\n”; return; } 里面有这么一段,看样子是用来验证一些东西。我直接把这段注释掉,WP-SuperCache后台就可以打开了。后面都很顺利,现在WP-SuperCache工作很正常。 大致浏览了一下,程序里很多判断都可以去掉(前提是你很了解它的原理了)。阉割之后,应该还会快一些呢。

APC引起500错误

昨天说了在Windows上面安装APC,我用Apache的ab.exe测试了一下之前和之后服务器生成页面的速度,提升确实很明显。但是很快遇到了问题:服务器经常出现500错误,网站怎么也打不开。 在日志里的错误信息是这样的: [client 208.36.144.6] (OS 121)信号灯超时时间已到  : FastCGI: failed to connect to server “D:/AppServ/php5/php-cgi.exe”: CreateFile()/WaitNamedPipe() timed out [Tue May 13 09:53:10 2008] [error] [client 208.36.144.6] FastCGI: incomplete headers (0 bytes) received from server “D:/AppServ/php5/php-cgi.exe” 搜了一下,没搜到解决办法。又是一个不眠之夜,今天早上只好把APC关掉,访问就正常了。想了一下,failed to connect to server的错误,可能是fastcgi进程因为APC需要不停地查询缓存、更新缓存、整理碎片而被拖慢了。这该如何解决呢?

Windows上安装APC

心血来潮去翻Fenng以前整理出来的大型网站们批露的架构方面的信息,看到的都是很典型的,基于ROR的财帮子(不晓得现在JavaEye流量大还是财帮子大),基于.net的“PlentyOfFish”,深藏不露的豆瓣以及非常平民化的facebook(用的是PHP+MySQL)。有点纳闷,这些网站怎么没一个用java的,虽然说是“Architecture Scale”,但看起来java确实不受待见阿。 翻到Facebook的时候注意到它用了Memcached和APC。Memcached我已经用在服务器上了,APC以前在Dreamhost时使用过,不过效果一般。这里谈到Facebook用APC使得“一个页面显示的时间从4000多毫秒降到了100多毫秒”,肯定不是所有用了APC的地方都能有这么大的提升,但是使用APC确实能为PHP加速。 由于PHP是个解释型语言,执行的时候先得把程序读进来,(进行基本的差错,)然后“解释”成机器可以跑的程序。解释就会生成Opcode,如果可以把这个Opcode缓存起来,下次就能避免重新解释了。想想一下很多大型的系统,一个页面会包含很多文件,所以把这些文件的opcode存储起来,效果有点像“片段缓存”。我所知道的可以做PHP中opcode缓存的除了APC还有XCache,今天就安装了APC在自己的服务器上。 借用下面这张图,应该能弄清楚APC的作用了。 网上有很多在linux服务器上安装APC的指导,其实作为PHP的扩展,在windows的安装大同小异。先去perl4win.php.net,搜索APC,就能找到可以下载的DLL文件。这时需要选择一个和服务器PHP版本相匹配的。我服务器上的PHP是5.2.3,就下载了最后一个DLL。 下载回来后放到PHP插件文件夹,一般是php目录下的”ext”文件夹。之后编辑php.ini,在末尾加入: [apc] extension=”php_apc.dll” apc.enabled=1 apc.shm_segments=1 apc.shm_size=48 apc.ttl=7200 apc.user_ttl=7200 apc.num_files_hint=1024 apc.mmap_file_mask=d:/tmp/apc.XXXXXX apc.enable_cli=1 其中apc.shm_size就是给APC开的缓存大小,单位是M。我之前开了16M,有点小,就改成了48M。保存php.ini之后重启apache,如果你有一个显示phpinfo()的网页,刷新就能看到安装完成的apc了。 如果想得到apc运行的更多信息,可以去下载一个APC源代码包,里面有一个apc.php,放在web目录下就可以看到APC运行的详细情况。见下面的图:

cookie中的AJSTAT_ok_times和AJSTAT_ok_pages

niupu缓存策略的使用上,为了省力用了Zend_Cache中的PageCache。而使用PageCache时,对于不同的登录用户,可能有一些细节是不一样,所以选择了“cache_with_cookie”和”make_id_with_cookie”。这样的话,程序会先对cookie数组进行序列化,将其转化为字符串,然后与REQUEST_URI拼接,最后计算md5散列值。计算得到的结果就是这个PageCache的ID。 对于一般的爬虫们发起的请求,自然是不带有Cookie的,也可以容易地使用缓存。但通过Firefox打开网站,在使用帐户登录以前,我也以为是不带有Cookie、可以使用缓存的。但实际情况是一直都不能成功使用缓存,以为是个案,所以没有管。直到前些日子用了memcached,又忍不住开工调试。 最后发现是cookie中的 AJSTAT_ok_times和AJSTAT_ok_pages在捣乱。在我没做任何设置时,这两个值就随着我访问网站出现在cookie列表里。我仔细搜索也没找到关于这两兄弟的细节,比如为啥会自己出现在cookie中。不过我猜测ok_times应该是记录访问这个网站的次数,ok_pages用来记录访问的页数,不知道是不是正确。 找到了问题,就可以有针对性地做出修改了。比较好的办法是写一个新的类继承原有的类,然后重写一下make_id这个函数。不过我就直接改了Zend_Cache的源码。 在/Cache/Frontend/Page.php中,有个叫“_makePartialId”的函数,找到包含case = “Cookie”的一节,其中有一句:  $var = $_COOKIE; 改成 $var = array($_COOKIE['info1'],$_COOKIE['info2']…); 原理就是用一个类似filter 的东西把有效的Cookie信息进行重新打包成一个数组。这样就不会把那些没用的Cookie信息也一并序列化了。 虽然问题解决了,可是还是没弄明白 AJSTAT_ok_times和AJSTAT_ok_pages到底是怎么生出来的。

命令行查看Memcached运行状态

很多时候需要监控服务器上的Memcached运行情况,比如缓存的查询次数,命中率之类的。但找到的那个memcached-tool是linux下用perl写的,我也没试过windows能不能用。后来发现个简单的办法可以做到,就是使用Telnet。 首先登录到服务器,然后在cmd命令行中键入 telnet 127.0.0.1 11211 其中127.0.0.1是服务器的地址(这里是本机) ,11211是memcached绑定的端口号。 之后命令行窗口全黑只有光标提示,摸黑输入stats,即可得到描述Memcached服务器运行情况的参数。如下图: 其中,uptime 是memcached运行的秒数,cmd_get是查询缓存的次数。这两个数据相除一下就能得到平均每秒请求缓存的次数——最近niupu的流量很低,所以平均也就一秒请求一次多,这么点大的压力,用文件系统缓存一样没问题,根本不会体现出使用memcached的优越。 下面的cmd_set 就是设置key=>value的次数。整个memcached是个大hash,用cmd_get没有找到的内容,就会调用一下cmd_set写进缓存里。紧跟着是get_hits,就是缓存命中的次数。缓存命中率 = get_hits/cmd_get * 100%。 下面的get_misses的数字加上get_hits应该等于cmd_get。而total_itemscurr_items表示现在在缓存中的键值对个数,在图上total_items == cmd_set == get_misses,不过当可用最大内存用光时,memcached就会删掉一些内容,上面的等式就不成立了。 话说回来,memcached要是能有一套完整的监测工具就太好了。memcached的安装和php相应配置请看这里。

缓存的效率

今天尝试着在牛扑的三个页面上用了缓存:tag页面,分类页面以及图书信息页面。中午没吃饭,把它搞了出来。刚刚放上去的时候,缓存之后的页面效率很高,基本上比原来的查询提高了一个数量级。 但是下午出去办了很多事情,晚上和徐老师聊了很久,再回来看牛扑,程序的效率已经让我惨不忍睹。一个简单的图书信息页面都在几十秒才能出来,而且是使用缓存之后──为什么呢? 原因在于缓存的文件太多了。 现在那个缓存目录下,大概有几万个文件。想像一下ZendCache需要在这几万个文件中准确找出那个对应的缓存文件,然后判断它是不是过期了,这中间的开销不亚于从百万级别的数据库中来检索(因为数据库的检索是可以进行优化的)。 现在那里边的文件已经多到我使用rm命令都删除不了了,相当恐怖。 当然,上次PHP之父Rasmus 说,缓存可以有很多层次。其实用ZendCache适合做通用的缓存,在缓存内容比较少的时候效率提升很明显。但是文件数目的增多会使效率急剧下降,所以,控制一个较短的lifetime可以算一个解决的办法了。 另外,牛扑上的图书目录所用的缓存就很稳定。看来这些东西还是应该自己好好去实践一下。通用的缓存架构不一定是提升效率的稻草。