php-fpm nginx 優(yōu)化[轉(zhuǎn)]

在實(shí)際的開發(fā)和應(yīng)用中偶發(fā)的502,504讓人頭痛,下面轉(zhuǎn)發(fā)一個(gè)寫的比較全面的。

PHP-fpm

PHP-FPM是一個(gè)PHPFastCGI管理器,是只用于PHP的。

php-fpm 已經(jīng)在 Linux、MacOSX、Solaris 和 FreeBSD 上測試通過。

確信 libxml2(在某些系統(tǒng)上叫做libxml2-devel)已經(jīng)安裝。

關(guān)于信號(hào)處理

SIGINT, SIGTERM立刻終止

SIGQUIT平滑終止

SIGUSR1重新打開日志文件

SIGUSR2平滑重載所有worker進(jìn)程并重新載入配置和二進(jìn)制模

參數(shù)調(diào)優(yōu)

進(jìn)程數(shù)

首先,我們關(guān)注一個(gè)前提設(shè)置: pm = static/dynamic,標(biāo)識(shí)fpm子進(jìn)程的產(chǎn)生模式

static(靜態(tài)) :表示在fpm運(yùn)行時(shí)直接fork出pm.max_chindren個(gè)worker進(jìn)程

dynamic(動(dòng)態(tài)):表示運(yùn)行時(shí)fork出start_servers個(gè)進(jìn)程,隨著負(fù)載的情況,動(dòng)態(tài)的調(diào)整,最多不超過max_children個(gè)進(jìn)程。

一般推薦用static,優(yōu)點(diǎn)是不用動(dòng)態(tài)的判斷負(fù)載情況,提升性能,缺點(diǎn)是多占用些系統(tǒng)內(nèi)存資源。

static:worker進(jìn)程pm.max_children = 300這個(gè)值原則上是越大越好

dynamic:worker進(jìn)程pm.start_servers = 20

dynamic:空閑狀態(tài)pm.min_spare_servers = 5最小php-fpm進(jìn)程數(shù)量

dynamic:空閑狀態(tài)pm.max_spare_servers = 35最大php-fpm進(jìn)程數(shù)量

max_children

這個(gè)值原則上是越大越好,php-cgi的進(jìn)程多了就會(huì)處理的很快,排隊(duì)的請求就會(huì)很少。

設(shè)置”max_children”也需要根據(jù)服務(wù)器的性能進(jìn)行設(shè)定

一般來說一臺(tái)服務(wù)器正常情況下每一個(gè)php-cgi所耗費(fèi)的內(nèi)存在20M左右

假設(shè)“max_children”設(shè)置成100個(gè),20M*100=2000M

也就是說在峰值的時(shí)候所有PHP-CGI所耗內(nèi)存在2000M以內(nèi)。

假設(shè)“max_children”設(shè)置的較小,比如5-10個(gè),那么php-cgi就會(huì)“很累”,處理速度也很慢,等待的時(shí)間也較長。

如果長時(shí)間沒有得到處理的請求就會(huì)出現(xiàn)504 Gateway Time-out這個(gè)錯(cuò)誤,而正在處理的很累的那幾個(gè)php-cgi如果遇到了問題就會(huì)出現(xiàn)502 Bad gateway這個(gè)錯(cuò)誤。

start_servers

pm.start_servers的默認(rèn)值為2。并且php-fpm中給的計(jì)算方式也為:

{(cpu空閑時(shí)等待連接的php的最小子進(jìn)程數(shù)) + (cpu空閑時(shí)等待連接的php的最大子進(jìn)程數(shù) - cpu空閑時(shí)等待連接的php的最小子進(jìn)程數(shù))/ 2};

用配置表示就是:min_spare_servers + (max_spare_servers - min_spare_servers) / 2;

一般而言,設(shè)置成10-20之間的數(shù)據(jù)足夠滿足需求了。

最大請求數(shù)max_requests

最大處理請求數(shù)是指一個(gè)php-fpm的worker進(jìn)程在處理多少個(gè)請求后就終止掉,master進(jìn)程會(huì)重新respawn一個(gè)新的。

這個(gè)配置的主要目的是避免php解釋器或程序引用的第三方庫造成的內(nèi)存泄露。

pm.max_requests = 10240

當(dāng)一個(gè) PHP-CGI 進(jìn)程處理的請求數(shù)累積到 max_requests 個(gè)后,自動(dòng)重啟該進(jìn)程。

502,是后端 PHP-FPM 不可用造成的,間歇性的502一般認(rèn)為是由于 PHP-FPM 進(jìn)程重啟造成的.

但是為什么要重啟進(jìn)程呢?

如果不定期重啟 PHP-CGI 進(jìn)程,勢必造成內(nèi)存使用量不斷增長(比如第三方庫有問題等)。因此 PHP-FPM 作為 PHP-CGI 的管理器,提供了這么一項(xiàng)監(jiān)控功能,對(duì)請求達(dá)到指定次數(shù)的 PHP-CGI 進(jìn)程進(jìn)行重啟,保證內(nèi)存使用量不增長。

正是因?yàn)檫@個(gè)機(jī)制,在高并發(fā)中,經(jīng)常導(dǎo)致 502 錯(cuò)誤

目前我們解決方案是把這個(gè)值盡量設(shè)置大些,減少 PHP-CGI 重新 SPAWN 的次數(shù),同時(shí)也能提高總體性能。PS:剛開始我們是500導(dǎo)致內(nèi)存飆高,現(xiàn)在改成5120,當(dāng)然可以再大一些,10240等,這個(gè)主要看測試結(jié)果,如果沒有內(nèi)存泄漏等問題,可以再大一些。

最長執(zhí)行時(shí)間request_terminate_timeout

max_execution_time和request_terminate_timeout

; The timeout for serving a single request after which the worker process will

; be killed. This option should be used when the ‘max_execution_time’ ini option

; does not stop script execution for some reason. A value of ‘0’ means ‘off’.

; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)

; Default Value: 0

;request_terminate_timeout = 0

============

設(shè)置單個(gè)請求的超時(shí)中止時(shí)間. 該選項(xiàng)可能會(huì)對(duì)php.ini設(shè)置中的’max_execution_time’因?yàn)槟承┨厥庠驔]有中止運(yùn)行的腳本有用. 設(shè)置為 ‘0’ 表示 ‘Off’.當(dāng)經(jīng)常出現(xiàn)502錯(cuò)誤時(shí)可以嘗試更改此選項(xiàng)。

這兩項(xiàng)都是用來配置一個(gè)PHP腳本的最大執(zhí)行時(shí)間的。當(dāng)超過這個(gè)時(shí)間時(shí),PHP-FPM不只會(huì)終止腳本的執(zhí)行,還會(huì)終止執(zhí)行腳本的Worker進(jìn)程。

Nginx會(huì)發(fā)現(xiàn)與自己通信的連接斷掉了,就會(huì)返回給客戶端502錯(cuò)誤。

內(nèi)存|CPU排查方法

top

命令格式:

top [-] [d] [p] [q] [c] [C] [S]? ? [n]

參數(shù)說明:

d: 指定每兩次屏幕信息刷新之間的時(shí)間間隔。當(dāng)然用戶可以使用s交互命令來改變之。

p: 通過指定監(jiān)控進(jìn)程ID來僅僅監(jiān)控某個(gè)進(jìn)程的狀態(tài)。

q:該選項(xiàng)將使top沒有任何延遲的進(jìn)行刷新。如果調(diào)用程序有超級(jí)用戶權(quán)限,那么top將以盡可能高的優(yōu)先級(jí)運(yùn)行。

S: 指定累計(jì)模式

s : 使top命令在安全模式中運(yùn)行。這將去除交互命令所帶來的潛在危險(xiǎn)。

i: 使top不顯示任何閑置或者僵死進(jìn)程。、

m:切換顯示內(nèi)存信息。

t:切換顯示進(jìn)程和CPU狀態(tài)信息。

c:切換顯示命令名稱和完整命令行。

M: 根據(jù)駐留內(nèi)存大小進(jìn)行排序。

P:根據(jù)CPU使用百分比大小進(jìn)行排序。

T:根據(jù)時(shí)間/累計(jì)時(shí)間進(jìn)行排序。

sar

執(zhí)行sar -P ALL 1 100。-P ALL表示監(jiān)控所有核心,1表示每1秒采集,100表示采集100次。

開啟慢日志

配置輸出php-fpm慢日志,閥值為2秒:

request_slowlog_timeout = 2

slowlog = log/$pool.log.slow

利用sort/uniq命令分析匯總php-fpm慢日志:

grep -v “^$” www.log.slow.tmp | cut -d ” ” -f 3,2 | sort | uniq -c | sort -k1,1nr | head -n 50

參數(shù)解釋:

sort: 對(duì)單詞進(jìn)行排序

uniq -c: 顯示唯一的行,并在每行行首加上本行在文件中出現(xiàn)的次數(shù)

sort -k1,1nr: 按照第一個(gè)字段,數(shù)值排序,且為逆序

head -10: 取前10行數(shù)據(jù)

用strace跟蹤進(jìn)程

利用nohup將strace轉(zhuǎn)為后臺(tái)執(zhí)行,直到attach上的php-fpm進(jìn)程死掉為止:

nohup strace -T -p 13167 > 13167-strace.log &

參數(shù)說明:

-c 統(tǒng)計(jì)每一系統(tǒng)調(diào)用的所執(zhí)行的時(shí)間,次數(shù)和出錯(cuò)的次數(shù)等.

-d 輸出strace關(guān)于標(biāo)準(zhǔn)錯(cuò)誤的調(diào)試信息.

-f 跟蹤由fork調(diào)用所產(chǎn)生的子進(jìn)程.

-o filename,則所有進(jìn)程的跟蹤結(jié)果輸出到相應(yīng)的filename

-F 嘗試跟蹤vfork調(diào)用.在-f時(shí),vfork不被跟蹤.

-h 輸出簡要的幫助信息.

-i 輸出系統(tǒng)調(diào)用的入口指針.

-q 禁止輸出關(guān)于脫離的消息.

-r 打印出相對(duì)時(shí)間關(guān)于,每一個(gè)系統(tǒng)調(diào)用.

-t 在輸出中的每一行前加上時(shí)間信息.

-tt 在輸出中的每一行前加上時(shí)間信息,微秒級(jí).

-ttt 微秒級(jí)輸出,以秒了表示時(shí)間.

-T 顯示每一調(diào)用所耗的時(shí)間.

-v 輸出所有的系統(tǒng)調(diào)用.一些調(diào)用關(guān)于環(huán)境變量,狀態(tài),輸入輸出等調(diào)用由于使用頻繁,默認(rèn)不輸出.

-V 輸出strace的版本信息.

-x 以十六進(jìn)制形式輸出非標(biāo)準(zhǔn)字符串

-xx 所有字符串以十六進(jìn)制形式輸出.

-a column

設(shè)置返回值的輸出位置.默認(rèn)為40.

-e execve 只記錄 execve 這類系統(tǒng)調(diào)用

-p 主進(jìn)程號(hào)

也可以用利用-c參數(shù)讓strace幫助匯總

strace -cp pid

PHP-fpm配置文件注釋

pid = run/php-fpm.pid

#pid設(shè)置,默認(rèn)在安裝目錄中的var/run/php-fpm.pid,建議開啟

error_log = log/php-fpm.log

#錯(cuò)誤日志,默認(rèn)在安裝目錄中的var/log/php-fpm.log

log_level = notice

#錯(cuò)誤級(jí)別. 可用級(jí)別為: alert(必須立即處理), error(錯(cuò)誤情況), warning(警告情況), notice(一般重要信息), debug(調(diào)試信息). 默認(rèn): notice.

emergency_restart_threshold = 60

emergency_restart_interval = 60s

#表示在emergency_restart_interval所設(shè)值內(nèi)出現(xiàn)SIGSEGV或者SIGBUS錯(cuò)誤的php-cgi進(jìn)程數(shù)如果超過 emergency_restart_threshold個(gè),php-fpm就會(huì)優(yōu)雅重啟。這兩個(gè)選項(xiàng)一般保持默認(rèn)值。

process_control_timeout = 0

#設(shè)置子進(jìn)程接受主進(jìn)程復(fù)用信號(hào)的超時(shí)時(shí)間. 可用單位: s(秒), m(分), h(小時(shí)), 或者 d(天) 默認(rèn)單位: s(秒). 默認(rèn)值: 0.

daemonize = yes

#后臺(tái)執(zhí)行fpm,默認(rèn)值為yes,如果為了調(diào)試可以改為no。在FPM中,可以使用不同的設(shè)置來運(yùn)行多個(gè)進(jìn)程池。 這些設(shè)置可以針對(duì)每個(gè)進(jìn)程池單獨(dú)設(shè)置。

listen = 127.0.0.1:9000

#fpm監(jiān)聽端口,即nginx中php處理的地址,一般默認(rèn)值即可。可用格式為: 'ip:port', 'port', '/path/to/unix/socket'. 每個(gè)進(jìn)程池都需要設(shè)置.

listen.backlog = -1

#backlog數(shù),-1表示無限制,由操作系統(tǒng)決定,此行注釋掉就行。backlog含義參考:http://www.3gyou.cc/?p=41

listen.allowed_clients = 127.0.0.1

#允許訪問FastCGI進(jìn)程的IP,設(shè)置any為不限制IP,如果要設(shè)置其他主機(jī)的nginx也能訪問這臺(tái)FPM進(jìn)程,listen處要設(shè)置成本地可被訪問的IP。默認(rèn)值是any。每個(gè)地址是用逗號(hào)分隔. 如果沒有設(shè)置或者為空,則允許任何服務(wù)器請求連接

listen.owner = www

listen.group = www

listen.mode = 0666

#unix socket設(shè)置選項(xiàng),如果使用tcp方式訪問,這里注釋即可。

user = www

group = www

#啟動(dòng)進(jìn)程的帳戶和組

pm = dynamic #對(duì)于專用服務(wù)器,pm可以設(shè)置為static。

#如何控制子進(jìn)程,選項(xiàng)有static和dynamic。如果選擇static,則由pm.max_children指定固定的子進(jìn)程數(shù)。如果選擇dynamic,則由下開參數(shù)決定:

pm.max_children #,子進(jìn)程最大數(shù)

pm.start_servers #,啟動(dòng)時(shí)的進(jìn)程數(shù)

pm.min_spare_servers #,保證空閑進(jìn)程數(shù)最小值,如果空閑進(jìn)程小于此值,則創(chuàng)建新的子進(jìn)程

pm.max_spare_servers #,保證空閑進(jìn)程數(shù)最大值,如果空閑進(jìn)程大于此值,此進(jìn)行清理

pm.max_requests = 1000

#設(shè)置每個(gè)子進(jìn)程重生之前服務(wù)的請求數(shù). 對(duì)于可能存在內(nèi)存泄漏的第三方模塊來說是非常有用的. 如果設(shè)置為 '0' 則一直接受請求. 等同于 PHP_FCGI_MAX_REQUESTS 環(huán)境變量. 默認(rèn)值: 0.

pm.status_path = /status

#FPM狀態(tài)頁面的網(wǎng)址. 如果沒有設(shè)置, 則無法訪問狀態(tài)頁面. 默認(rèn)值: none. munin監(jiān)控會(huì)使用到

ping.path = /ping

#FPM監(jiān)控頁面的ping網(wǎng)址. 如果沒有設(shè)置, 則無法訪問ping頁面. 該頁面用于外部檢測FPM是否存活并且可以響應(yīng)請求. 請注意必須以斜線開頭 (/)。

ping.response = pong

#用于定義ping請求的返回相應(yīng). 返回為 HTTP 200 的 text/plain 格式文本. 默認(rèn)值: pong.

request_terminate_timeout = 0

#設(shè)置單個(gè)請求的超時(shí)中止時(shí)間. 該選項(xiàng)可能會(huì)對(duì)php.ini設(shè)置中的'max_execution_time'因?yàn)槟承┨厥庠驔]有中止運(yùn)行的腳本有用. 設(shè)置為 '0' 表示 'Off'.當(dāng)經(jīng)常出現(xiàn)502錯(cuò)誤時(shí)可以嘗試更改此選項(xiàng)。

request_slowlog_timeout = 10s

#當(dāng)一個(gè)請求該設(shè)置的超時(shí)時(shí)間后,就會(huì)將對(duì)應(yīng)的PHP調(diào)用堆棧信息完整寫入到慢日志中. 設(shè)置為 '0' 表示 'Off'

slowlog = log/$pool.log.slow

#慢請求的記錄日志,配合request_slowlog_timeout使用

rlimit_files = 1024

#設(shè)置文件打開描述符的rlimit限制. 默認(rèn)值: 系統(tǒng)定義值默認(rèn)可打開句柄是1024,可使用 ulimit -n查看,ulimit -n 2048修改。

rlimit_core = 0

#設(shè)置核心rlimit最大限制值. 可用值: 'unlimited' 、0或者正整數(shù). 默認(rèn)值: 系統(tǒng)定義值.

chroot =

#啟動(dòng)時(shí)的Chroot目錄. 所定義的目錄需要是絕對(duì)路徑. 如果沒有設(shè)置, 則chroot不被使用.

chdir =

#設(shè)置啟動(dòng)目錄,啟動(dòng)時(shí)會(huì)自動(dòng)Chdir到該目錄. 所定義的目錄需要是絕對(duì)路徑. 默認(rèn)值: 當(dāng)前目錄,或者/目錄(chroot時(shí))

catch_workers_output = yes

#重定向運(yùn)行過程中的stdout和stderr到主要的錯(cuò)誤日志文件中. 如果沒有設(shè)置, stdout 和 stderr 將會(huì)根據(jù)FastCGI的規(guī)則被重定向到 /dev/null . 默認(rèn)值: 空.

總結(jié)篇

502

max_children

request_terminate_timeout、max_execution_time

數(shù)據(jù)庫

網(wǎng)關(guān)服務(wù)是否啟動(dòng)如php-fpm

504

錯(cuò)誤主要查看nginx.conf關(guān)于網(wǎng)關(guān)如fastcgi的配置

request_terminate_timeout設(shè)置0或者過長問題(502)

如果設(shè)置為0或者過長的時(shí)間,可能會(huì)引起file_get_contents的資源問題。

如果file_get_contents請求的遠(yuǎn)程資源如果反應(yīng)過慢,file_get_contents就會(huì)一直卡在那里不會(huì)超時(shí)

我們知道php.ini 里面max_execution_time 可以設(shè)置 PHP 腳本的最大執(zhí)行時(shí)間,

但是,在 php-cgi(php-fpm) 中,該參數(shù)不會(huì)起效。真正能夠控制 PHP 腳本最大執(zhí)行時(shí)間的是 php-fpm.conf 配置文件中的request_terminate_timeout參數(shù)。

修改該參數(shù),設(shè)置 PHP 腳本最大執(zhí)行時(shí)間是必要的,但是,治標(biāo)不治本。例如改成 30s,如果發(fā)生 file_get_contents() 獲取網(wǎng)頁內(nèi)容較慢的情況,這就意味著 150 個(gè) php-cgi 進(jìn)程,每秒鐘只能處理 5 個(gè)請求,WebServer 同樣很難避免”502 Bad Gateway”。

解決辦法是request_terminate_timeout設(shè)置為10s或者一個(gè)合理的值,或者給file_get_contents加一個(gè)超時(shí)參數(shù)。

max_requests參數(shù)配置不當(dāng),可能會(huì)引起間歇性502錯(cuò)誤

php-fpm的慢日志,debug及異常排查神

參考資料

1)php-fpm優(yōu)化方法匯總

http://blog.haohtml.com/archives/11162

2)多Sock文件和php-fpm實(shí)例配置

http://xn–ghqyhzj.com/post-21537.html

Nginx+PHP-FPM優(yōu)化技巧總結(jié)

http://blog.csdn.net/dc_726/article/details/12340349

https://blog.tanteng.me/2016/03/php-fpm-conf/

https://www.zybuluo.com/phper/note/89081

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容