建立時間:2008-11-09 01:12:51 最後修改時間:2008-11-09 01:12:51
php
本文發表在《程序員》雜誌2008年第11期
PHP沉思錄之六:Drupal的性能問題
左輕侯
Drupal是一個基於PHP的開源CMS系統,也是我認爲技術上實現得最好的一個PHP應用。Drupal的架構很是優秀,經過微內核+plugin的方式,實現了極佳的擴展性,從而使Drupal遠遠超出通常的CMS這一範疇。從這個意義上來講,把Drupal稱爲Web OS彷佛更加合適一些。關於Drupal,有太多的話能夠說,也許我會在之後的時間裏寫一篇文章對它進行專門的討論。可是在本文中,我想討論的,是Drupal社區中的每個人都會面對,但不是每個人都對其有清晰認識的問題,即Drupal的性能問題。
由於客戶需求,我曾經對Drupal作過比較全面的測試。當時的環境是雙服務器(DB server+Web Server),硬件配置都是單CPU+4G。數據庫裏面有幾千條Node記錄。用JMeter對各類狀況下(開/關各類cache模塊,logged user/anonymous user)不一樣頁面的讀取和寫入操做都進行過測試。
測試的結果可能和不少人印象中不同。兩個主要的結果以下:
1. Logged user和anonymous user的性能差距很是大。同一個頁面,logged user的RPS(Requests per second)通常不超過20,而啓用了cache的anonymous user的RPS在100多,當使用了file-based cache之後,甚至能超過300。
2. 數據庫壓力相對較小。因爲Drupal把大量可配置的內容都放在數據庫中,所以每每容易產生這樣一種印象,即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+的時候,即便每一個請求佔20M內存,也只有200M+內存而已。
2. DB server和Web server分開,或者加強DB server的配置。一臺中等性能的mysql服務器,應付200~300的併發是很輕鬆的事情,在併發數只有10+的時候,db server其實是很空閒的。
3. 基礎軟件的優化,例如從Windows轉移到Linux,從apache轉移到Lighttpd,從MySQL遷移到其餘數據庫,除了從Windows轉移到 Linux會有比較明顯的提高之外(由於PHP在Linux上的效率比在Windows上要好),其它的措施可能會快一些,但不會有大幅度的提升,由於瓶頸不在那裏。
有明顯做用的:
1. 使用APC/XCache這樣的代碼優化程序,速度會有幾倍的提高。估計你們都已經這樣作過了。
2. 增長web server的CPU數量。雙核的確定比單核的快,4個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的,不須要每次都從新加載。這裏有個測試結果:http://www.workhabit.org/resin-backed-php-drives-4x-performance-improvements-drupal 。Drupal在Quercus下有4倍的性能提升,可是這個數字跟Drupal在打開APC/eAccelerator下的提高差很少,因此可能沒有太大的實用價值。
另一種思路是代碼自己的優化。
使用cache API基本上是沒有意義的,由於對於logger user,Drupal不會調用cache API。Drupal.org上有人提出,即便是logged user,有不少頁面也是不用定製化的,這意味着能夠cache它們。可是Drupal沒有提供這樣一種機制。只要是logged user,Drupal就會執行完整的bootstrap過程,即便只打印出一個hello world,所以實際上你沒有辦法在logged user狀態下cache單個頁面。
到目前最新版本的Drupal(Drupal 6.4)爲止,對於logger user,Drupal只提供了一種cache功能,就是能夠將部分block設置爲可cache的。在block佔用大量服務器時間的狀況下,block cache可以有效地提升效率。可是,因爲block cache對於bootstrap過程並沒有影響,所以當瓶頸在於bootstrap自己時,Block cache是無能爲力的。
在Drupal.org的社區,關於logger user的cache問題,一直處於熱烈的討論之中。基本的結論是,因爲Drupal的架構就是這樣,目前沒有很好的解決方案,只能期待Drupal在之後的版本中進行改進了。
我研究了一下Drupal的bootstrap過程,發現也許這樣是可行的:實現hook_boot函數,這是bootstrap中執行最靠前的一個函數,它被調用時,bootstrap的大部分過程尚未執行。在hook_boot中,檢查當前頁面是否須要cache,若是是,直接讀 cache生成頁面,而後調用exit()強行結束。這在理論上是可行的,但太過hack了一點。
Drupal的狀況是這樣,那麼其餘的PHP框架,尤爲是半官方的Zend Framework,性能如何呢?經過搜索,我在網上找到了一份PHP framework comparison benchmarks,網址見:http://www.avnetlabs.com/php/php-framework-comparison-benchmarks。根據這份報告的數據,Zend Framework的性能只有原生PHP的10%,若是沒有用APC,連3%都不到。固然,這份報告的數據不必定詳盡,Zend Framework在不一樣環境下的表現應該也會有出入。可是,Zend Framework的性能大幅度落後於Baseline PHP,應該是確鑿無疑的。
爲何PHP主流框架的性能都存在着這樣的問題呢?其實這也不難理解。回顧PHP沉思錄系列第一篇中對於PHP工做模型的討論,因爲PHP沒有駐留內存的進程,因此每個request發生時,都必須初始化全部的對象,這致使大量的時間被耗費在進程代碼的執行過程當中。當PHP程序僅僅是簡單的腳本時,這可有可無,可是在結構複雜的架構中,因爲每次處理request都要重複調用成千上萬行代碼,這一問題就變得很是突出了。並且,除非PHP之後的版本對這一機制進行改進,不然這個問題沒法獲得完全的解決。
那麼,這是否意味着,PHP只能適用於小型的網站,而沒法在高併發量的大型網站施展拳腳呢?固然不是這樣。事實上,在Yahoo和其餘許多知名的巨型網站上,都大量地使用了PHP。緣由在於,PHP僅僅被用做一個內容生成器,生成的內容會被轉化爲靜態文本,絕大多數用戶瀏覽的都是被cache的靜態文本。這就和PHP程序的性能毫無關係了。可是,當用戶並非僅僅進行瀏覽,而是須要頻繁地和網站進行互動時,PHP的性能不但沒法比擬C和Java,甚至沒法與同爲腳本語言的Python和Ruby相比。也就是說,PHP更適合於新聞門戶這樣的內容發佈站點,而不是web 2.0應用的首選。
在本系列文章告一段落的時候,咱們看到的是PHP的侷限性。熱愛PHP的人們可能會對此以爲沮喪。可是,這並沒有損於PHP做爲一門優秀語言的聲譽。尺有所短,寸有所長,對於咱們熟悉和喜好的工具,咱們更應該瞭解它們的侷限,這也有利於咱們更有效地使用它們。mysql