原文地址:https://wzfou.com/php-fpm/
最近發現博客的內存老是隔三差五地被“吃掉”了,登錄到后台后偶爾會出卡頓的情況,一開始懷疑是Swap不夠導致的,於是給VPS主機增加了幾個G的Swap,觀察了一段時間后發現再大的Swap也被慢慢地“吃掉”了!
很顯然是PHP某些服務一直在占用着VPS的內存沒有釋放,導致物理內存耗盡后調用了Swap,顯然Swap沒有物理內存運行的效率高,於是就出現了進程卡死的情況了。考慮到挖站否現在用的Wordpress用的主題與插件過多,出現這樣的情況也是正常。
LNMP架構中PHP是運行在FastCGI模式下,按照官方的說法,php-cgi會在每個請求結束的時候會回收腳本使用的全部內存,但是並不會釋放給操作系統,而是繼續持有以應對下一次PHP請求。而php-fpm是FastCGI進程管理器,用於控制php的內存和進程等。
所以,解決的辦法就是通過php-fpm優化總的進程數和單個進程占用的內存,從而解決php-fpm進程占用內存大和不釋放內存的問題。
一. 分析判斷php-fpm內存占用情況
如果你發現VPS主機出現了卡頓的情況,首先查看一下內存的占用情況,常用的命令就是Top、Glances、Free等,不了解這些命令的朋友可以先看看挖站否做的專題:Linux系統監控命令整理匯總-掌握CPU,內存,磁盤IO等找出性能瓶頸。
使用Glances命令,再按下m,就可以查看到當前VPS主機進程內存占用情況了,按照占用內存由多到少排序(或者使用Top命令,按下M,效果是一樣的)。如下圖(點擊放大):
這是一張重啟后進程內存占用情況圖,從前后對比中可以發現:隨着開機時間的增長,php-fpm占用的內存越來越大,最終php-fpm耗盡了VPS所有物理內存。
查看當前php-fpm總進程數,命令:
ps -ylC php-fpm --sort:rss
ps -fe |grep "php-fpm"|grep "pool"|wc -l
其中RSS就是占用的內存情況。如下圖:
查看當前php-fpm進程的內存占用情況及啟動時間,命令如下:
ps -e -o 'pid,comm,args,pcpu,rsz,vsz,stime,user,uid'|grep www|sort -nrk5
從下圖可以看出當前php-fpm所有進程平均每個進程占用了60-70MB的內存,啟動時間,是當天的話就是3:12,否則會顯示是X月X日。
查看當前php-fpm進程平均占用內存情況,一般來說一個php-fpm進程占用的內存為30-40MB,本次查詢的結果是60MB,顯然是多了。命令如下:
ps --no-headers -o "rss,cmd" -C php-fpm | awk '{ sum+=$1 } END { printf ("%d%s\n", sum/NR/1024,"M") }'
結果61M
二. 熟悉php-fpm配置文件說明
php-fpm.conf就是php-fpm的配置文件,路徑一般在:/usr/local/php/etc
,如下圖:
php-fpm.conf幾個重要的參數說明如下:
pm = dynamic #指定進程管理方式,有3種可供選擇:static、dynamic和ondemand。 pm.max_children = 16 #static模式下創建的子進程數或dynamic模式下同一時刻允許最大的php-fpm子進程數量。 pm.start_servers = 10 #動態方式下的起始php-fpm進程數量。 pm.min_spare_servers = 8 #動態方式下服務器空閑時最小php-fpm進程數量。 pm.max_spare_servers = 16 #動態方式下服務器空閑時最大php-fpm進程數量。 pm.max_requests = 2000 #php-fpm子進程能處理的最大請求數。 pm.process_idle_timeout = 10s request_terminate_timeout = 120
pm三種進程管理模式說明如下:
pm = static,始終保持一個固定數量的子進程,這個數由pm.max_children定義,這種方式很不靈活,也通常不是默認的。 pm = dynamic,啟動時會產生固定數量的子進程(由pm.start_servers控制)可以理解成最小子進程數,而最大子進程數則由pm.max_children去控制,子進程數會在最大和最小數范圍中變化。閑置的子進程數還可以由另2個配置控制,分別是pm.min_spare_servers和pm.max_spare_servers。如果閑置的子進程超出了pm.max_spare_servers,則會被殺掉。小於pm.min_spare_servers則會啟動進程(注意,pm.max_spare_servers應小於pm.max_children)。 pm = ondemand,這種模式和pm = dynamic相反,把內存放在第一位,每個閑置進程在持續閑置了pm.process_idle_timeout秒后就會被殺掉,如果服務器長時間沒有請求,就只會有一個php-fpm主進程。弊端是遇到高峰期或者如果pm.process_idle_timeout的值太短的話,容易出現504 Gateway Time-out錯誤,因此pm = dynamic和pm = ondemand誰更適合視實際情況而定。
三、解決php-fpm進程占用內存大問題
3.1調整管理模式
static管理模式適合比較大內存的服務器,而dynamic則適合小內存的服務器,你可以設置一個pm.min_spare_servers和pm.max_spare_servers合理范圍,這樣進程數會不斷變動。ondemand模式則更加適合微小內存,例如512MB或者256MB內存,以及對可用性要求不高的環境。
3.2減少php-fpm進程數
如果你的VPS主機的內存被占用耗盡,可以檢查一下你的php-fpm進程數,按照php-fpm進程數=內存/2/30來計算,1GB內存適合的php-fpm進程數為10-20之間,具體還得根據你的PHP加載的附加組件有關系。
3.3php-fpm配置示例
這里以1GB內存的VPS配置php-fpm為演示,實際操作來看設置數值還得根據服務器本身的性能、PHP等綜合考慮。
pm = dynamic #dynamic和ondemand適合小內存。 pm.max_children = 15 #static模式下生效,dynamic不生效。 pm.start_servers = 8 #dynamic模式下開機的進程數量。 pm.min_spare_servers = 6 #dynamic模式下最小php-fpm進程數量。 pm.max_spare_servers = 15 #dynamic模式下最大php-fpm進程數量。
四、解決php-fpm進程不釋放內存問題
上面通過減少php-fpm進程總數來達到減少php-fpm內存占用的問題,實際使用過程中發現php-fpm進程還存長期占用內存而不釋放的問題。解決的方法就是減少pm.max_requests數。
最大請求數max_requests,即當一個 PHP-CGI 進程處理的請求數累積到 max_requests 個后,自動重啟該進程,這樣達到了釋放內存的目的了。以1GB內存的VPS主機設置為例(如果你設置的數值沒有達到釋放內存可以繼續調低):
pm.max_requests = 500
當php-fpm進程達到了pm.max_requests設定的數值后,就會重啟該進程,從而釋放內存。下圖是我測試后的效果,可以看出php-fpm進程被強制結束並釋放了內存。
五、總結
對於大內存以及對並發和可用性要求的話,建議使用static管理模式+最大的pm.max_children。如果是小內存的服務器,建議使用dynamic或者ondemand模式,同時降低pm.start_servers和pm.max_spare_servers進程數。
為什么我調整了參數沒有達到應有的效果?根據wzfou.com的經驗,php-fpm配置文件參數不能一概而論,必須要結合服務器自身的性能、WEB動態內容以及對可用性的要求來進行調整,內存長期占用最好是再檢查一下是否有內存泄露。