我因为工作原因,曾经对Drupal做过比较全面的测试。当时的环境是双服务器(DB server+Web Server),硬件配置都是单CPU+4G。数据库里面有几千条记录。用JMeter对各种情况下(开/关各种cache模块,logged user/anonymous user)不同页面的读取和写入操作都进行过测试。
测试的结论可能和很多人印象中不一样。都说Drupal对数据库要求高,但事实上,无论是cache还是非cache模式,DB server的压力都是相当小的(CPU在10%以下),而Web Server的CPU在80%以上。跟踪所有的db query的执行时间后,也证明了这一点(全部db query的执行时间只占页面生成时间的一小部分)。所以结论是,Drupal在大量logged user并发情况下的瓶颈,在于执行Drupal代码的CPU时间。
之所以出现这种情况,和PHP本身的执行机制和Drupal的实现方式有关。Drupal在生成一个非cached的页面时,不管这个页面多么简单,都要执行一个完整的bootstrap过程,即使只启用了最少的模块,这个过程也要调用几十个PHP文件,执行成千上万行PHP代码。而PHP的机制又决定了没有任何PHP代码或者对象能够驻留内存,每次响应请求都必须执行完整的初始化工作。
而anonymous user之所以快,是因为Drupal在执行cached page的时候,不会执行完整的bootstrap过程,它先检查是否cached page,是的话就读取缓存,然后结束工作。这样当然就快了。
以这个结论为前提,可以解释一些事情:
1 为什么Drupal的性能在各种环境下相差并不多。无论是双服务器,单服务器,甚至内存非常小的虚拟机,logged user的RPS值总是在10~20之间。数据库里面有几百条,或者几十万条记录,影响也不大。因为瓶颈并不在于DB或者内存,而是在于执行代码的过程。
2 为什么使用APC/XCache这样的代码优化程序,能够得到极大的性能提升。在我自己的虚机环境上,RPS从3~4提升到了12。因为它提升的是PHP代码的执行时间。
从这个结论出发,列出一些对优化Drupal的logged user性能有明显作用和没有明显作用的措施:
没有明显作用的:
1 加内存。在并发数只有10+的时候,即使每个请求占10M内存,也只有100M+内存而已。
2 DB server和Web server分开,或者增强DB server的配置。一台中等性能的mysql服务器,应付200~300的并发是很轻松的事情,在并发数只有10+的时候,db server实际上闲得没事干。
3 基础软件的优化,例如从Windows转移到Linux,从apache转移到httpd,从MySQL迁移到其他数据库,除了从Windows转移到Linux会有比较明显的提升以外(因为提升了PHP的执行效率),其它的措施可能会快一些,但不会有数量级的提高,因为瓶颈不在那里。
有明显作用的:
1 使用APC/XCache这样的代码优化程序,速度会有几倍的提升。估计大家都已经这样做过了。
2 增加web server的CPU数量。双核的肯定比单核的快,8个CPU肯定比2个CPU快得多。
3 使用多web server+单db server的配置,把代码执行的压力分散到不同的web server上。上文说到,单台db server可以轻松应付200+的并发,这意味着理论上可以支持10台以上的web server。
4 使用Quercus这样的引擎,把PHP代码编译成Java,再在Java VM中运行,理论上会有很大的提高。原因是,第一,Java的运行效率比PHP高,第二,Java代码是可以cache的,不需要每次都重新加载。这里有个测试结果:
Drupal在Quercus下有4倍的性能提高,但是这个数字跟Drupal在打开APC/eAccelerator下的提升差不多,所以没有太大的价值。
另外一种思路是代码本身的优化。
使用cache API没有任何意义,因为logger user根本不用调用它。
Drupal.org上有人提出,即使是logged user,有很多页面也是不用定制化的,这意味着可以cache它们。但是Drupal没有提供这样一种机制。只要是logged user,Drupal就会执行完整的bootstrap过程,即使只打印出一个hello world,因此实际上你没有办法在logged user状态下cache单个页面。
我研究了一下Drupal的bootstrap过程,发现也许这样是可以的:实现hook_boot函数,这是bootstrap中执行最靠前的一个函数,它被调用时,bootstrap的大部分过程还没有执行。在hook_boot中,检查当前页面是否需要cache,如果是,直接读cache生成页面,然后调用exit()强行结束。这应该是可行的,但太过hack了一点。
另外,说说我对Drupal的看法。Drupal是我见过的web应用里面架构做得最好的一个(不仅仅是PHP,也包括Java和.NET应用)。在没有见过Drupal之前,我就想过,如果能把Eclipse的实现思路用web平台的方式实现,通过微内核+plugin实现所有的功能,那是一个多么方便的事情。接触Drupal以后,发现这就是我在寻找的东西。
从性能上来说,Drupal在anonymous user的情况下是非常优秀的,根据我以前的测试,基于file-based cache的插件,RPS在350以上,这意味着单机就可以实现万人在线的站点了。但是,Drupal在logged user的情况下性能不佳,这也是事实。RPS 10+可能也可以应付几百人在线的情况,但是想想同等配置下,原生PHP和Java都能到100+,还是很让人郁闷啊。
阅读(1326) | 评论(0) | 转发(0) |