php類方法線上效能測試,php類效能測試
在兩個月前一個群裡的朋友問了一個問題,他說:“現在他們公司的項目有一個模組的效能線上表現非常差,很長時間沒有查出問題所在,老闆一怒之下讓他把所有類方法的執行時間給記錄進行分析,並且不能影響現在的項目效能。”老闆讓他記錄這些資訊是為了分析具體影響效能的地方在哪些地方,待項目運行一段時間就去除。這個需求導致兩個個問題,第一是怎麼監聽這個模組所有類方法的執行時間,第二是怎麼能在不影響現在項目效能的情況下完成(本身效能就很差了),下面我們就這兩個問題來分析:
一、怎麼監聽這個模組所有類方法的執行時間
這個問題他第一個想到的是在所有類方法處理前加一段代碼記錄時間,在返回資料前進行計算已耗用時間,然後記錄日誌。這種方法一定是可行的,而且對效能的影響不是很大,但是…… 反正我不會這麼幹了,程式員都是懶惰的,而且都是很聰明的,我們不是碼農,如果這麼實施,那麼要改動的代碼量太大了,重複很久沒有意義沒有技術含量的工作,其次以後刪除的時候又來一次,我會奔潰。當然位朋友也不會這麼幹,所以我才知道了這個奇葩的需求。
怎麼解決呢,本來這麼任務解決起來確實很費勁,我們可以借用__call()和__callStatic()對類方法進行重載,在php5.3以前靜態方法只能自己一個個去加了,感謝php5.3新增了__callStatic()魔術方法。有人會問了這個兩個魔術方法都是在類方法不存在的時候才有用,他們怎麼能實現這個需求呢?這個問題等會看代碼,下面分析第二個問題吧。
二、怎麼能在不影響現在項目效能的情況下完成
為啥我說會有這個問題呢?之前分析需求的時候確實有,但是在回答第一個問題的時候已經說瞭解決方案,我認為採用__call()和__callStatic()對類方法進行重載,比較簡單實現,且對現有項目效能影響很小。
我們在這個問題裡主要討論一些其它方法,其實實現效能分析的擴充也有很多,xdebug、xhprof等,都知道xdebug效能損耗很大,不適合在正式環境使用,xhprof效能損耗相對較小,可在正式環境使用。那為什麼不用xhprof呢?有三點:1.效能損耗略大;2.日誌記錄格式不靈活,對日誌的分析造成困擾;3.有些函數沒法統計,而且會出現嚴重錯誤(如:call_user_func_array)
既然已經確定解決方案,我們就先看一個demo吧
/** * 類方法效能監聽 * * @author 戚銀 thinkercode@sina.com * @date2015-05-31 22:09 */class demo {/** * 普通類方法 * * @access public * @return void */public function test1(){for($i=0; $i<100; ++$i){}}/** * 靜態方法 * * @access public * @return void */public static function test2(){for($i=0; $i<100; ++$i){}}}
看上面的demo類,其中有兩個方法,一個普通方法,一個靜態方法,我們業務層調用的方式如下:
<?php(new demo())->test1();demo::test2();
我們保證一個原則就是不改變類以外的代碼,只調整該類來實現,下面我採用__call()和__callStatic()對類方法進行重載。
<?php/** * 類方法效能監聽 * * @author 戚銀 thinkercode@sina.com * @date2015-05-31 22:09 */class demo {/** * 普通類方法 * * @access public * @return void */public function _test1(){for($i=0; $i<100; ++$i){}}/** * 靜態方法 * * @access public * @return void */public static function _test2(){for($i=0; $i<100; ++$i){}}/** * 類魔術方法 * * @access public * @param string $name * @param array $arguments * @return mixed */public function __call($name, $arguments){$startTime = microtime(true);$data = call_user_func_array(array($this, '_'.$name), $arguments);echo microtime(true) - $startTime;return $data;}/** * 類魔術方法 * * @access public * @param string $name * @param array $arguments * @return mixed */public static function __callStatic($name, $arguments){$startTime = microtime(true);$data = call_user_func_array(array(__CLASS__, '_'.$name), $arguments);echo microtime(true) - $startTime;return $data;}}
在這段代碼裡面我們添加了__call()和__callStatic()方法,如果只添加這兩個方法是沒用的,因為之前業務層的代碼沒變,調用的方法是存在的,要使調用的方法不存在而且只改變類本身,就只能在方法名前加一個底線(這個規則自己定),然後我們在調用這個兩個方法發現輸出了兩個方法的執行時間。
這樣實現也發現了幾個問題,改動的代碼還是很多,每個類都要加,很累…………,其實善用手邊的工具很好實現,使用繼承是個很好的方式,把這個兩個方法寫到一個基類裡,然後所有類都這繼承這個基類。類方法名替換,除了構造方法和析構方法,直接使用編輯器批量替換即可,以後改回來也是。
注意:如果使用繼承方式實現,__callStatic()方法的__CLASS__需要調整。
這裡以在類方法前增加底線實現,使業務層找不到類方法,其實這個例子還可以調整方法可見度來實現,但是可見度實現的方式有以下弊端:
1. 調整後以後想調整回來很有可能弄錯類方法可見度,不知道哪些方法調整了。
2. 調整可見度只對類公用方法有效,對受保護和私用的方法不可用
當然如果只記錄類公用方法的效能,可以使用改變方法可見度實現,但一定記得在注視上添加標註該方法是改變過的,而且一定被把public改成private,因為如果有類繼承這個類就沒法訪問這個方法了。