可能是 php-fpm 本身的問題或是某個 extension 造成
不過可以改一些參數,讓有問題的 process 早一點投胎(自行消滅),不要一直佔著毛坑
PHP-FPM 主要的設定:
pm = dynamic
pm.max_children = 100 #這個值 要依據記憶體總量來計算,後面說明
pm.start_servers = 5 #default 5 ,官方公式 = min_spare_servers + (max_spare_servers - min_spare_servers) / 2
pm.min_spare_servers = 5 #default 5 The desired minimum number of idle server processes.
pm.max_spare_servers = 35 #default 35 The desired maximum number of idle server processes.
pm.max_requests = 300 #default 0
這個很重要,不要設為 0
是指單一個 php-fpm process 最多累積處理多少次 requests
當一個 process 處理的連線數 (requests) 到達此數字時,
就強制關閉此 process ,並在產生另一個新的 process
主要是避免 memory leak 的問題
看 php-fpm 記憶體耗用情形,若耗用嚴重 就設定小一點 如 100~300,讓他早死早超生
(低、中、高流量網站 得視情況挑整此數字)
(ps: 在 apache httpd 中 也有個類似的參數 MaxConnectionsPerChild )
pm.process_idle_timeout = 10s #default 10s The number of seconds after which an idle process will be killed.
以上 pm.max_children 、pm.max_requests 兩個特別需要注意
計算 pm.max_children 方式
先查詢目前 php-fpm 用多少記憶體
這是 php-fpm 剛執行時的資料 $ ps -ylC php-fpm --sort:rss S UID PID PPID C PRI NI RSS SZ WCHAN TTY TIME CMD S 48 20058 30058 0 80 0 6444 347320 - ? 00:00:00 php-fpm S 48 20059 30058 0 80 0 6444 347320 - ? 00:00:00 php-fpm S 48 20061 30058 0 80 0 6444 347320 - ? 00:00:00 php-fpm S 0 30058 1 0 80 0 24040 347320 - ? 00:00:54 php-fpm S 48 20057 30058 24 80 0 58560 350132 - ? 00:00:00 php-fpm S 48 20060 30058 6 80 0 59096 350132 - ? 00:00:00 php-fpm 這是 php-fpm 執行 1小時的資料 RSS 平均變成 300MB $ ps -ylC php-fpm --sort:rss S UID PID PPID C PRI NI RSS SZ WCHAN TTY TIME CMD S 0 30058 1 0 80 0 24120 347320 - ? 00:00:54 php-fpm S 48 20080 30058 2 80 0 123888 351156 - ? 00:00:01 php-fpm S 48 20061 30058 3 80 0 128344 351193 - ? 00:00:01 php-fpm S 48 20058 30058 3 80 0 133544 352271 - ? 00:00:01 php-fpm S 48 20060 30058 1 80 0 138920 352217 - ? 00:00:00 php-fpm S 48 20081 30058 2 80 0 139492 351180 - ? 00:00:01 php-fpm S 48 20057 30058 5 80 0 157816 370652 - ? 00:00:02 php-fpm S 48 20059 30058 2 80 0 166856 370883 - ? 00:00:01 php-fpm S 48 20076 30058 3 80 0 177448 352553 - ? 00:00:01 php-fpmRSS (resident set size) 行即是 php-fpm 用的記憶體(不含swap),單位 kB
*RSS 即記憶體佔用量(kb) 因process之間會共享記憶體 所以此數字會偏大
RSS 相當於 top 畫面中的 SHR,參考資料
算出 php-fpm "平均"使用多少記憶體: $ ps --no-headers -o "rss" -C php-fpm | awk '{ sum+=$1 } END { printf ("%d%s\n", sum/NR/1024,"M") }' 172M 算出 php-fpm "總"使用多少記憶體: $ ps --no-headers -o "rss" -C php-fpm | awk '{ sum+=$1 } END { printf ("%d%s\n", sum/1024,"M") }' 8744M **RSS算出來 並非真正占用的記憶體數量
機器有 16GB 記憶體,扣除作業系統以及其它程式需要的記憶體數量
大約剩 13GB
(13G*1024) / 172M = 77 = pm.max_children
(若懶的計算,這裡有表格幫你自動算出 pm.max_children )
參考:
Adjusting child processes for PHP-FPM (Nginx)
An Introduction to PHP-FPM Tuning
另外,每個 php-fpm 每個 process 的 requests 數目,可以在監控介面中查詢
參考
Nginx 啟用 PHP-FPM 服務狀態監控網頁教學
補充
這是比對 php-fpm status 畫面 (http://xxx/status ) 中的資料
幾個 process 處理不到 300 requests 就耗用 500+MB 的記憶體
S UID PID PPID C PRI NI RSS SZ WCHAN TTY TIME CMD S 48 2470 21320 0 80 0 549284 340628 - ? 00:00:33 php-fpm requests: 297 S 48 4287 21320 0 80 0 542868 341143 - ? 00:00:25 php-fpm requests: 234 S 48 6691 21320 0 80 0 518856 341706 - ? 00:00:19 php-fpm requests: 169 S 48 15925 21320 0 80 0 214956 319260 - ? 00:00:01 php-fpm requests: 18 S 48 15938 21320 0 80 0 214564 338446 - ? 00:00:01 php-fpm S 48 16270 21320 0 80 0 238236 340770 - ? 00:00:01 php-fpm S 48 16283 21320 0 80 0 201124 340169 - ? 00:00:00 php-fpm S 48 16287 21320 0 80 0 234524 339745 - ? 00:00:01 php-fpm S 48 16299 21320 0 80 0 215240 340266 - ? 00:00:00 php-fpm S 48 16437 21320 0 80 0 221740 340098 - ? 00:00:01 php-fpm S 0 21320 1 0 80 0 28732 316150 - ? 00:00:04 php-fpm
php-fpm status 設定方式
apache httpd 設定:
<LocationMatch "/php_fpm_status">
Order Allow,Deny
Allow from 127.0.0.1 xxx.xxx.xxx.xxx
ProxyPass unix:/var/run/php-fpm/www.sock|fcgi://localhost/php_fpm_status
</LocationMatch>
php-fpm 設定
pm.status_path = /php_fpm_status
重啟 httpd / php-fpm 即可瀏覽
http:/xxx.xxx.xxx.xxx/php_fpm_status?full
畫面類似:
https://github.com/php/php-src/issues/9687
這裡一段 php 程式 用來測試記憶體洩漏 (memory leak) 的問題
<?php for ($i = 0; $i < 10; $i++) { $test = new Test(); $test->test_function(); echo memory_get_usage()/1024/1024; echo " MB\n"; } class Test { public $data = ''; public function __construct() { } public function test_function() { // Generate 4 MB data inside the object $this->data = str_repeat('a', 4*1024*1024); self::test_function2($this); } public static function test_function2($object) { //echo 'Hello'; // Enable this to prevent memory leak return; } } /* php 7.4.33 cli 4.3774185180664 MB 4.3774490356445 MB 4.3774490356445 MB 4.3774490356445 MB 4.3774490356445 MB 4.3774490356445 MB 4.3774490356445 MB 4.3774490356445 MB 4.3774490356445 MB 4.3774490356445 MB php 7.4.33 cli (**跟上一個測試 屬不同的主機) 4.3415145874023 MB 8.3455276489258 MB 12.349510192871 MB 16.353492736816 MB 20.357475280762 MB 24.361457824707 MB 28.365440368652 MB 32.369422912598 MB 36.373405456543 MB 40.377388000488 MB php-fpm v7.4.33 4.3382415771484 MB 8.3422241210938 MB 12.346206665039 MB 16.350189208984 MB 20.35417175293 MB 24.358154296875 MB 28.36213684082 MB 32.366119384766 MB 36.370101928711 MB 40.374084472656 MB php-fpm v8.1.27 4.340446472168 MB 4.340446472168 MB 4.340446472168 MB 4.340446472168 MB 4.340446472168 MB 4.340446472168 MB 4.340446472168 MB 4.340446472168 MB 4.340446472168 MB 4.340446472168 MB php-fpm v8.2.15 4.3403778076172 MB 4.3403778076172 MB 4.3403778076172 MB 4.3403778076172 MB 4.3403778076172 MB 4.3403778076172 MB 4.3403778076172 MB 4.3403778076172 MB 4.3403778076172 MB 4.3403778076172 MB */