PHP FastCGI進程管理器PHP-FPM的架構 PHP FastCGI進程管理器PHP-FPM的架構

PHP FastCGI進程管理器PHP-FPM的架構

 

 
一個master進程,支持多個pool,每一個pool由master進程監聽不一樣的端口,pool中有多個worker進程. 
每一個worker進程都內置PHP解釋器,而且進程常駐後臺,支持prefork動態增長. 
每一個worker進程支持在運行時編譯腳本並在內存中緩存生成的opcode來提高性能. 
每一個worker進程支持配置響應指定請求數後自動重啓,master進程會重啓掛掉的worker進程. 
每一個worker進程能保持一個到MySQL/Memcached/Redis的持久鏈接,實現"鏈接池",避免重複創建鏈接,對程序透明. 
使用數據庫持久鏈接時應該設置固定數量的worker進程數,不要使用動態的prefork模式. 

經 @syaokun219 和 @IM鑫爺 糾正,如下兩句有誤: 
master進程採用epoll模型異步接收和分發請求,listen監聽端口,epoll_wait等待鏈接. 
而後分發給對應pool裏的worker進程,worker進程accpet請求後poll處理鏈接. 
應該是: 
master進程並不接收和分發請求,而是worker進程直接accpet請求後poll處理. php

經查看源代碼結構,php已經支持epoll模型,監聽時epoll,accept後poll
master進程不斷調用epoll_wait和getsockopt是用來異步處理信號事件和定時器事件. 
這裏提一下,Nginx也相似,master進程並不處理請求,而是worker進程直接處理, 
不過區別在於Nginx的worker進程是epoll異步處理請求,而PHP-FPM仍然是poll. 

若是worker進程不夠用,master進程會prefork更多進程, 
若是prefork達到了pm.max_children上限,worker進程又全都繁忙, 
這時master進程會把請求掛起到鏈接隊列backlog裏(默認值是511). 

1個PHP-FPM工做進程在同一時刻裏只能處理1個請求. 
MySQL的最大鏈接數max_connections默認是151. 
只要PHP-FPM工做進程數不超過151,就不會出現鏈接不上MySQL的狀況. 
並且正常狀況下,也不須要開啓那麼多的PHP-FPM工做進程, 
好比4個PHP-FPM進程就能跑滿4個核心的CPU, 
那麼你開40個PHP-FPM進程也沒有任何意義, 
只會佔用更多的內存,形成更多的CPU上下文切換,性能反而更差. 
爲了減小每一個請求都重複創建和釋放鏈接的開銷,能夠開啓持久鏈接, 
一個PHP-FPM進程保持一個到MySQL的長鏈接,實現透明的"鏈接池". 

Nginx跟PHP-FPM分開,實際上是很好的解耦,PHP-FPM專門負責處理PHP請求,一個頁面對應一個PHP請求, 
頁面中全部靜態資源的請求都由Nginx來處理,這樣就實現了動靜分離,而Nginx最擅長的就是處理高併發. 
PHP-FPM是一個多進程的FastCGI服務,相似Apache的prefork的進程模型, 
對於只處理PHP請求來講,這種模型是很高效很穩定的. 
不像Apache(libphp.so),一個頁面,要處理多個請求,包括圖片,樣式表,JS腳本,PHP腳本等. 

php-fpm從5.3開始才進入PHP源代碼主幹,以前版本沒有php-fpm. 
那時的spawn-fcgi是一個須要調用php-cgi的FastCGI進程管理器, 
另外像Apache的mod_fcgid和IIS的PHP Manager也須要調用php-cgi進程, 
但php-fpm則根本不依賴php-cgi,徹底獨立運行,也不依賴php(cli)命令行解釋器. 
由於php-fpm是一個內置了php解釋器的FastCGI服務,啓動時可以自行讀取php.ini配置和php-fpm.conf配置. 

我的認爲,PHP-FPM工做進程數,設置爲2倍CPU核心數就足夠了. 
畢竟,Nginx和MySQL以及系統一樣要消耗CPU. 
根據服務器內存來設置PHP-FPM進程數很是不合理, 
把內存分配給MySQL,Memcached,Redis,Linux磁盤緩存(buffers/cache)這些服務顯然更合適. 
過多的PHP-FPM進程反而會增長CPU上下文切換的開銷. 
PHP代碼中應該儘可能避免curl或者file_get_contents這些可能會產生較長網絡I/O耗時的代碼. 
注意設置CURLOPT_CONNECTTIMEOUT_MS超時時間,避免進程被長時間阻塞. 
若是要異步執行耗時較長的任務,能夠 pclose(popen('/path/to/task.php &', 'r')); 打開一個進程來處理, 
或者藉助消息隊列,總之就是要儘可能避免阻塞到PHP-FPM工做進程. 
在php-fpm.conf中把request_slowlog_timeout設爲1秒,在slowlog中查看是否有耗時超過1秒的代碼. 
優化代碼,可以爲全部PHP-FPM工做進程減負,這個纔是提升性能的根本方法. 

能讓CPU滿負荷運行的操做能夠視爲CPU密集型操做. 
上傳和下載則是典型的I/O密集型操做,由於耗時主要發生在網絡I/O和磁盤I/O. 
須要PHP認證的下載操做能夠委託爲Nginx的AIO線程池: 
header("X-Accel-Redirect: $file_path"); 
至於上傳操做,好比能夠創建一個監聽9001端口的名爲upload的PHP-FPM進程池(pool), 
專門負責處理上傳操做(經過Nginx分發),避免上傳操做阻塞到監聽9000端口的計算密集的www進程池. 
這時upload進程池多開點進程也無所謂. 
nginx.conf: 
location = /upload.php { 
    include fastcgi_params; 
    fastcgi_pass 127.0.0.1:9001
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 

php-fpm.conf: 
[www] 
listen = 127.0.0.1:9000 
pm = static 
pm.max_children = 4 
[upload] 
listen = 127.0.0.1:9001 
pm = dynamic 
pm.max_children = 8 
pm.start_servers = 4 
pm.min_spare_servers = 4 
pm.max_spare_servers = 4 
其中IO密集這個進程池[io]採用動態的prefork進程,好比這裏是繁忙時8個,空閒時4個. 
利用PHP-FPM提供的池的隔離性,分離計算密集和I/O密集操做,能夠減小阻塞對整個PHP應用的影響. 

補充: 
info.php 
<?php 
if( isset($_POST['submit']) ) { 
    header('Content-Type: text/plain; charset=utf-8'); 
    //chmod 777 uploads 
    move_uploaded_file($_FILES['upload_file']['tmp_name'], 'uploads/'.$_FILES['upload_file']['name']); 
    print_r($_FILES['upload_file']); 
    exit(); 
} else { 
    header('Content-Type: text/html; charset=utf-8'); 

?> 
<!DOCTYPE HTML> 
<html> 
    <head> 
        <meta charset="utf-8"> 
        <title>PHP文件上傳測試</title> 
    </head> 
    <body> 
        <!-- enctype="multipart/form-data" 以二進制格式POST傳輸數據 --> 
        <form action="<?php echo pathinfo(__FILE__)['basename']; ?>" method="POST" enctype="multipart/form-data"> 
            <div>文件1 <input type="file" name="upload_file" /></div> 
            <div><input type="submit" name="submit" value="提交" /></div> 
        </form> 
    </body> 
</html> 
Nginx和PHP-FPM的工做進程各自只開1個. 
以2KB每秒上傳圖片: 
time trickle -s -u 2 curl \ 
-F "action=info.php" \ 
-F "upload_file=@linux.jpeg;type=image/jpeg" \ 
-F "submit=提交" \ 
http://www.example.com/app/info.php 
sudo netstat -antp|egrep "curl|nginx|fpm" 
發現只有nginx和curl處於ESTABLISHED狀態,nginx和fpm都沒有被阻塞. 
top -p 4075 可見Nginx單線程. 
sudo strace -p 4075 可見Nginx調用recvfrom接收數據而且pwrite保存數據. 
sudo strace -p 13751 可見PHP-FPM是在Nginx接收完成用戶上傳的數據時才獲取數據. 
既然如此,我設想的另開PHP-FPM進程池處理上傳操做的用處就不是太大了. 
在文件上傳過程當中PHP-FPM並不會被阻塞,由於Nginx接收完上傳的內容後才一次性交給PHP-FPM. 
附:以2KB每秒下載圖片 
time trickle -s -d 2 \ 
wget http://www.example.com/app/uploads/linux.jpeg -O /dev/null html

http://www.cnblogs.com/huanxiyun/articles/5413755.html
相關文章
相關標籤/搜索