1. 程式人生 > >淺談 PHP

淺談 PHP

        這篇部落格分三個部分陳述,分別是PHP程序管理器、PHP啟動流程和PHP優化。

 

        談到PHP程序管理器,不得不講下它的進化過程,CGI--->FastCGI--->PHP-FPM。

 

        CGI全稱是“公共閘道器介面”(Common Gateway Interface),是外部應用程式(CGI程式)與Web伺服器之間的介面標準。

 

        以APP呼叫CRM服務介面為例,當web伺服器收到此請求後,根據配置檔案知道這不是一個靜態檔案,需要去找PHP直譯器來處理。於是,web伺服器會把這個請求經過相關處理後交給PHP直譯器。接下來PHP直譯器會解析php.ini檔案,初始化執行環境,然後處理請求,再以CGI規定的格式返回處理後的結果,退出程序。web伺服器再把結果返回給客戶端。

 

        CGI方式在遇到連線請求先要建立CGI的子程序,啟用一個CGI程序,然後處理請求,處理完後結束這個子程序。這就是fork-and-execute模式。所以用CGI方式的伺服器有多少連線請求就會有多少CGI子程序,子程序反覆載入是CGI效能低下的主要原因。當用戶請求數量非常多時,會大量擠佔系統的資源如記憶體,CPU時間等,造成效能低下。

 

        FastCGI是一個可伸縮地、高速地在HTTP server和動態指令碼語言間通訊的介面, 是CGI的一種改進方案。與傳統的CGI不用的是,FastCGI像是一個常駐(long-live)型的CGI,它可以一直執行著,只要啟用後,不會每次都要花費時間去fork一次。具體表現為:FastCGI會先啟一個master,解析配置檔案,初始化執行環境,然後再啟動多個worker。當請求過來時,master會傳遞給一個worker,然後立即可以接受下一個請求。這樣就避免了重複的勞動,效率自然是高。

 

        PHP-FPM是一個實現了FastCGI的程式,PHP5.3.3已經整合php-fpm了,不再是第三方的包了,在./configure的時候帶 –enable-fpm引數即可開啟PHP-FPM。PHP-FPM提供了更好的PHP程序管理方式,可以有效控制記憶體和程序、可以平滑過載PHP配置。

 

       PHP-FPM程序管理器自身初始化,啟動多個CGI直譯器程序等待來自Nginx的請求。當客戶端請求達到PHP-FPM,管理器選擇到一個CGI程序進行處理,Nginx將CGI環境變數和標準輸入傳送到一個PHP-CGI子程序。PHP-CGI子程序處理完成後,將標準輸出返回給Nginx,當PHP-CGI子程序關閉連線時,請求處理完成。PHP-CGI子程序等待著下一個連線。

 

        下面可以看看PHP-FPM的常用配置。

        

;listen = 127.0.0.1:9000 nignx與php-fpm通訊地址
;修改之後會有502錯誤,fastcgi錯誤
listen = /dev/shm/php-cgi.sock

location ~ .*\.(php|php5)?$  {
    #fastcgi_pass  127.0.0.1:9000;
    #/dev/shm/是linux下一個非常有用的目錄,因為這個目錄不在硬碟上,而是在記憶體裡。
    fastcgi_pass   unix:/dev/shm/php-cgi.sock;
    fastcgi_index index.php;
    include fastcgi.conf;
}

;動靜子程序
pm = dynamic/static:程序管理
pm.max_children:static模式下建立的子程序數或dynamic模式下同一時刻允許最大的php-fpm子程序數量
pm.start_servers:動態方式下的起始php-fpm程序數量
pm.min_spare_servers:動態方式下伺服器空閒時最小php-fpm程序數量
pm.max_spare_servers:動態方式下伺服器空閒時最大php-fpm程序數量
pm.max_requests:php-fpm子程序能處理的最大請求數
;指定了一個php-fpm子程序執行多少次之後重啟該程序,nginx會報502

 

 

 

        PHP啟動過程分兩部分講,SAPI和PHP生命週期。

 

        SAPI是Server Application Programming Interface(伺服器應用程式設計介面)的縮寫。PHP通過SAPI提供了一組介面,供應用和PHP核心之間進行資料互動。PHP提供了一個函式檢視當前SAPI介面型別:php_sapi_name(cli/cli-server 命令列模式/apache2handler Apache載入PHP以模組模式/fastcgi Nignx+FastCGI 模式)。

 

        

 

        生命週期:無論使用哪種SAPI,在PHP執行指令碼前後,都包含一系列事件:Module的Init(MINT)和Shutdown(MSHUTDOWN),Request 的Init(RINT)和Shutdown(RSHUTDOWN)。

 

 

        第一階段是PHP模組初始化階段(MINT),可以初始化擴充套件內部變數、分配資源和註冊資源處理器,在整個PHP例項生命週期內,該過程只執行一次。使用get_loaded_extensions 函式來檢視所有編譯並載入的模組/擴充套件,相當於CLI模式下的php -m。

 

        第二階段是請求初始化階段(RINT),在模組初始化並激活後,會建立PHP執行環境,同時呼叫所有模組註冊的RINT函式,呼叫每個擴充套件的請求初始化函式 ,設定特定的環境變數、分配資源或執行其他任務,如稽核。

 

        第三階段,請求處理完成後,會呼叫PHP_RSHUTDOWN_FUNCTION進行回收,這是每個擴充套件的請求關閉函式,執行最後的清理工作。Zend引擎執行清理過程、垃圾收集、對之前的請求期間用到的每個變數執行unset。請求完成可能是執行到指令碼完成,也可能是呼叫die()或exit()函式完成。
 

        第四階段,當PHP生命週期結束時候,PHP_MSHUTDOWN_FUNCTION對模組進行回收處理,這是每個擴充套件的模組關閉函式,用於關閉自己的核心子系統。
 

        

單程序下PHP生命週期

 

多程序下PHP生命週期

 

多執行緒下PHP生命週期

 

        最後,談談PHP優化,分為zval結構、opcache和程式碼優化三部分。

 

        先介紹下PHP中變數及其記憶體物件的內部表示。PHP變數劃分為兩類:標量型別和複雜型別。標量型別包括布林型、整型、浮點型和字串;複雜型別包括陣列、物件和資源;還有一個NULL比較特殊,它不劃分為任何型別,而是單獨成為一類。所有這些型別,在PHP內部統一用一個叫做zval的結構表示,在PHP原始碼中這個結構名稱為“_zval_struct”。zval的具體定義在PHP原始碼的“Zend/zend.h”檔案中。

 

 

        其中聯合體“_zvalue_value”用於表示PHP中所有變數的值,這裡之所以使用union,是因為一個zval在一個時刻只能表示一種型別的變數。可以看到_zvalue_value中只有5個欄位,但是PHP中算上NULL有8種資料型別,那麼PHP內部是如何用5個欄位表示8種類型呢?這算是PHP設計比較巧妙的一個地方,它通過複用欄位達到了減少欄位的目的。例如,在PHP內部布林型、整型及資源(只要儲存資源的識別符號即可)都是通過lval欄位儲存的;dval用於儲存浮點型;str儲存字串;ht儲存陣列(注意PHP中的陣列其實是雜湊表);而obj儲存物件型別;如果所有欄位全部置為0或NULL則表示PHP中的NULL,這樣就達到了用5個欄位儲存8種類型的值。

 

 

        值得一提的是,PHP5中zval佔24個位元組,而PHP7中只佔16個位元組,PHP RC7已於本月12號放出,本來是放正式版本的,此版本效能比PHP5提升一倍,值得期待。

 

PHP7的zval結構

 

PHP7的效能測試結果,效能壓測結果,耗時從2.991下降到1.186,大幅度下降60%。

 

        PHP程式碼經過編譯後,會生成opcache碼,由Zend引擎執行。由於PHP是解釋型語言,每次執行PHP都會重新編譯,這會造成一定得效能損耗。那麼,把opcache儲存起來,豈不是更快了。Alternative PHP Cache (APC) 是一個開放自由的PHP opcode 快取。它的目標是提供一個自由、 開放,和健全的框架用於快取和優化PHP的中間程式碼。

 

 

 

        說起PHP優化來,有兩大法寶,基本上不用改什麼程式碼,效能就蹭蹭提升了很多,法寶為PHP版本升級和opcache,示例可以見上面兩幅圖。

 

        目前主流的框架大多是php程式碼寫的,這裡推薦個C擴充套件的框架,Yaf(Yet another framework),PHP啟動的時候便載入到記憶體,速度效能遠比PHP寫的框架強很多。此框架由目前國內最權威的PHP專家鳥哥開發維護,效能沒的說,在老東家微博工作時就用的是此框架,也可以見鳥哥關於此框架的Yaf的效能對比測試部落格

 

        善用xhprofxdebug檢視PHP程式碼中的效能瓶頸。

 

        下面就羅列了下,PHP的主要優化寫法

 

1、儘可能的使用PHP內部函式

2、能用PHP內部字串操作函式的情況下,儘量用他們,不要用正則表示式; 因為其效率高於正則

3、在迴圈之前設定迴圈的最大次數,而非在在迴圈中

4、儘量使用單引號

5、在組織大字串時多試試heredoc、nowdoc寫法

6、最好不用@,用@掩蓋錯誤會降低指令碼執行速度

7、用單引號代替雙引號來包含字串,這樣做會更快一些

8、使用++$i遞增

9、不要隨便就複製變數,會增加記憶體消耗

10、迴圈裡不要套SQL查詢,SQL查詢時不要用MySQL自帶的函式,交給PHP來處理

11、使用xhprof或xdebug檢視效能

12、快取,Memcached MongoDB Redis

13、……

 

 

 

 

 

        【ps】此部落格由PPT整理而成,插圖均由網上搜集,文章內容部分也參照網上優秀文章而來。