PHP-FPM配置
PHP-FPM
先來了解一些名詞概念:
CGI是Common Gateway Interface(通用網管協議),用於讓互動程式和Web伺服器通訊的協議。它負責處理URL的請求,啟動一個程序,將客戶端傳送的資料作為輸入,由Web伺服器收集程式的輸出並加上合適的頭部,再發送回客戶端。
FastCGI是基於CGI的增強版本的協議,不同於建立新的程序來服務請求,使用持續的程序和建立的子程序來處理一連串的程序,這些程序由FastCGI伺服器管理,開銷更小,效率更高。
PHP-FPM是PHP實現的FastCGI Process Manager(FastCGI程序管理器), 用於替換PHP FastCGI的大部分附加功能,適用於高負載網站。支援的功能如:
-
平滑停止/啟動的高階程序管理功能
-
慢日誌記錄指令碼
-
動態/靜態子程序產生
-
基於php.ini的配置檔案
PHP-FPM在5.4之後已經整合進入PHP原始碼中,提供更好的PHP程序管理方式,可以有效控制記憶體和程序,平滑過載PHP配置。如果需要使用,在./configure的時候帶上-enable-fpm引數即可,使用PHP-FPM來控制FastCGI程序:
// 支援start/stop/quit/restart/reload/logrotate引數
// quit/reload是平滑終止和平滑重新載入,即等現有的服務完成
./php-fpm --start
PHP-FPM配置
PHP-FPM配置檔案為php-fpm.conf,在這個配置檔案中我們需要了解一些引數。下面所有的子程序均指php-fpm程序,可以在終端通過ps aux | grep php檢視到。
-
顯示php-fpm: pool www的代表work子程序(實際處理請求)
-
顯示php-fpm: process master的代表master主程序(負責管理work子程序)
全域性配置
先看PHP-FPM最重要的全域性配置部分:
emergency_restart_threshold
如果在emergency_restart_interval設定的時間內收到該引數設定次數的SIGSEGV或SIGBUS退出的訊號,則FPM會重新啟動。預設值為0,表示關閉該功能。
emergency_restart_interval
設定平滑重啟的間隔時間,有助於解決加速器中共享記憶體的使用問題。可用單位s(預設)/m/h/d,預設值為0, 表示關閉。
process.max
FPM能夠建立的最大子程序數量,它在使用多個pm = dynamic配置的php-fpm pool程序池的時候,控制全域性的子程序數量。預設值為0,代表著無限制。
程序池配置
PHP-FPM的配置其餘部分是一個名為Pool Definitions的區域,這個區域的配置設定每個PHP-FPM程序池,程序池中是一系列相關的子程序。這部分開頭都是[程序池名稱],如[www]。
此時可以解釋看到ps aux | grep php中顯示的是php-fpm: pool www。
pm
pm指的是process manager,指定程序管理器如何控制子程序的數量,它為必填項,支援3個值:
-
static: 使用固定的子程序數量,由pm.max_children指定
-
dynamic:基於下面的引數動態的調整子程序的數量,至少有一個子程序
-
pm.max_chidren: 可以同時存活的子程序的最大數量
-
pm.start_servers: 啟動時建立的子程序數量,預設值為min_spare_servers + max_spare_servers - min_spare_servers) / 2
-
pm.min_spare_servers: 空閒狀態的子程序的最小數量,如果不足,新的子程序會被自動建立
-
pm.max_spare_servers: 空閒狀態的子程序的最大數量,如果超過,一些子程序會被殺死
-
-
ondemand: 啟動時不會建立子程序,當新的請求到達時才建立。會使用下面兩個引數:
-
pm.max_children
-
pm.process_idle_timeout 子程序的空閒超時時間,如果超時時間到沒有新的請求可以服務,則會被殺死
-
pm.max_requests
每一個子程序的最大請求服務數量,如果超過了這個值,該子程序會被自動重啟。在解決第三方庫的記憶體洩漏問題時,這個引數會很有用。預設值為0,指子程序可以持續不斷的服務請求。
PHP-FPM配置優化
PHP-FPM管理的方式是一個master主程序,多個pool程序池,多個worker子程序。其中每個程序池監聽一個socket套接字。具體的圖示:
其中的worker子程序實際處理連線請求,master主程序負責管理子程序:
1. `master`程序,設定1s定時器,通過`socket`檔案監聽
2. 在`pm=dynamic`時,如果`idle worker`數量<`pm.min_spare_servers`,建立新的子程序3. 在`pm=dynamic`時,如果`idle worker`數量>`pm.max_spare_servers`,殺死多餘的空閒子程序
4. 在`pm=ondemand`時,如果`idle worker`空閒時間>`pm.process_idle_timeout`,殺死該空閒程序
5. 當連線到達時,檢測如果`worker`數量>`pm.max_children`,列印`warning`日誌,退出;如果無異常,使用`idle worker`服務,或者新建`worker`服務
保障基本安全
我們為了避免PHP-FPM主程序由於某些糟糕的PHP程式碼掛掉,需要設定重啟的全域性配置:
; 如果在1min內有10個子程序被中斷失效,重啟主程序emergency_restart_threshold = 10emergency_restart_interval = 1m
程序數調優
每一個子程序同時只能服務一次連線,所以控制同時存在多少個程序數就很重要,如果過少會導致很多不必要的重建和銷燬的開銷,如果過多又會佔用過多的記憶體,影響其他服務使用。
我們應該測試自己的PHP程序使用多少記憶體,一般來說剛啟動時是8M左右,執行一段時間由於記憶體洩漏和快取會上漲到30M左右,所以你需要根據自己的預期記憶體大小設定程序的數量。同時根據程序池的數量來看一個程序管理器的子程序數量限制。
測試平均PHP子程序佔用的記憶體:
$ps auxf | grep php | grep -v grep
work 26829 0.0 0.0 715976 4712 ? Ss Jul11 0:00 php-fpm: master process (./etc/php-fpm.conf)
work 21889 0.0 0.0 729076 29668 ? S 03:12 0:20 \_ php-fpm: pool www
work 21273 0.0 0.0 728928 31380 ? S 03:25 0:21 \_ php-fpm: pool www
work 15114 0.0 0.0 728052 29084 ? S 03:40 0:19 \_ php-fpm: pool www
work 17072 0.0 0.0 728800 34240 ? S 03:54 0:22 \_ php-fpm: pool www
work 22763 0.0 0.0 727904 20352 ? S 11:29 0:04 \_ php-fpm: pool www
work 38545 0.0 0.0 727796 19484 ? S 12:34 0:01 \_ php-fpm: pool www
// 共佔用的記憶體數量
$ps auxf | grep php | grep -v grep | grep -v master | awk '{sum+=$6} END {print sum}'162712
// 所有的子程序數量
$ ps auxf | grep php | grep -v grep | grep -v master | wc -l
6
可以看到第6列,每一個子程序的記憶體佔用大概在19-34M之間(單位為KB)。平均的記憶體佔用為162712KB/6 = 27.1M。
檢視伺服器總的記憶體大小
$ free -g
total used free shared buffers cached
Mem: 157 141 15 0 4 123
-/+ buffers/cache: 13 143Swap: 0 0 0
可以看出我的伺服器總得記憶體大小是157G(-g採用了G的單位)。
程序數限制
此時如果我們分配全部的記憶體給PHP-FPM使用,那麼程序數可以限制在157000/27 = 5814,但是由於我的伺服器同時服務了很多內容,所以我們可以向下調整到512個程序數:
process.max = 512
pm = dynamic
pm.max_children = 512
pm.start_servers = 16
pm.min_spare_servers = 8
pm.max_spare_serveres = 30
防止記憶體洩漏
由於糟糕的外掛和庫,記憶體洩漏時有發生,所以我們需要對每一個子程序服務的請求數量做限制,防止無限制的記憶體洩漏:
pm.max_requests = 1000
重啟
如果上面的配置都按照你的實際需求和環境配置好了,不要忘記重啟PHP-FPM服務。