前言:php
1.少安裝PHP模塊, 費內存html
2.調高linux內核打開文件數量,可使用這些命令(必須是root賬號)(我是修改/etc/rc.local,加入ulimit -SHn 51200的)mysql
echo `ulimit -HSn 65536` >> /etc/profile echo `ulimit -HSn 65536` >> /etc/rc.local source /etc/profile
若是`ulimit -n`數量依舊很少(即上面配置沒生效)的話, 能夠在 /etc/security/limits.conf 文件最後加上linux
* soft nofile 51200 * hard nofile 51200
1.與Nginx使用Unix域Socket通訊(Nginx和php-fpm在同一臺服務器)nginx
Unix域Socket由於不走網絡,的確能夠提升Nginx和php-fpm通訊的性能,但在高併發時會不穩定。程序員
Nginx會頻繁報錯:connect() to unix:/dev/shm/php-fcgi.sock failed (11: Resource temporarily unavailable) while connecting to upstreamweb
能夠經過下面兩種方式提升穩定性:
1)調高nginx和php-fpm中的backlog
配置方法爲:在nginx配置文件中這個域名的server下,在listen 80後面添加default backlog=1024。
同時配置php-fpm.conf中的listen.backlog爲1024,默認爲128。
2)增長sock文件和php-fpm實例數
再新建一個sock文件,在Nginx中經過upstream模塊將請求負載均衡到兩個sock文件背後的兩套php-fpm實例上。sql
2.php-fpm參數調優緩存
pm = dynamic; 表示使用哪一種進程數量管理方式服務器
dynamic表示php-fpm進程數是動態的,最開始是pm.start_servers指定的數量,若是請求較多,則會自動增長,保證空閒的進程數不小於pm.min_spare_servers,若是進程數較多,也會進行相應清理,保證多餘的進程數很少於pm.max_spare_servers
static表示php-fpm進程數是靜態的, 進程數自始至終都是pm.max_children指定的數量,再也不增長或減小
pm.max_children = 300; 靜態方式下開啓的php-fpm進程數量
pm.start_servers = 20; 動態方式下的起始php-fpm進程數量
pm.min_spare_servers = 5; 動態方式下的最小php-fpm進程數量
pm.max_spare_servers = 35; 動態方式下的最大php-fpm進程數量
若是pm爲static, 那麼其實只有pm.max_children這個參數生效。系統會開啓設置數量的php-fpm進程
若是pm爲dynamic, 那麼pm.max_children參數失效,後面3個參數生效。系統會在php-fpm運行開始的時候啓動pm.start_servers個php-fpm進程,而後根據系統的需求動態在pm.min_spare_servers和pm.max_spare_servers之間調整php-fpm進程數
那麼,對於咱們的服務器,選擇哪一種pm方式比較好呢?事實上,跟Apache同樣,運行的PHP程序在執行完成後,或多或少會有內存泄露的問題。這也是爲何開始的時候一個php-fpm進程只佔用3M左右內存,運行一段時間後就會上升到20-30M的緣由了。
對於內存大的服務器(好比8G以上)來講,指定靜態的max_children實際上更爲穩當,由於這樣不須要進行額外的進程數目控制,會提升效率。由於頻繁開關php-fpm進程也會有時滯,因此內存夠大的狀況下開靜態效果會更好。數量也能夠根據 內存/30M 獲得,好比8GB內存能夠設置爲100,那麼php-fpm耗費的內存就能控制在 2G-3G的樣子。若是內存稍微小點,好比1G,那麼指定靜態的進程數量更加有利於服務器的穩定。這樣能夠保證php-fpm只獲取夠用的內存,將很少的內存分配給其餘應用去使用,會使系統的運行更加暢通。
對於小內存的服務器來講,好比256M內存的VPS,即便按照一個20M的內存量來算,10個php-cgi進程就將耗掉200M內存,那系統的崩潰就應該很正常了。所以應該儘可能地控制php-fpm進程的數量,大致明確其餘應用佔用的內存後,給它指定一個靜態的小數量,會讓系統更加平穩一些。或者使用動態方式,由於動態方式會結束掉多餘的進程,能夠回收釋放一些內存,因此推薦在內存較少的服務器或VPS上使用。具體最大數量根據 內存/20M 獲得。好比說512M的VPS,建議pm.max_spare_servers設置爲20。至於pm.min_spare_servers,則建議根據服務器的負載狀況來設置,比較合適的值在5~10之間。
在4G內存的服務器上200就能夠(個人1G測試機,開64個是最好的,建議使用壓力測試獲取最佳值)
pm.max_requests = 10240;
nginx php-fpm配置過程當中最大問題是內泄漏出問題:服務器的負載不大,可是內存佔用迅速增長,很快吃掉內存接着開始吃交換分區,系統很快掛掉!其實根據官方的介紹,php-cgi不存在內存泄漏,每一個請求完成後php-cgi會回收內存,可是不會釋放給操做系統,這樣就會致使大量內存被php-cgi佔用。
官方的解決辦法是下降PHP_FCGI_MAX_REQUESTS的值,若是用的是php-fpm,對應的php-fpm.conf中的就是max_requests,該值的意思是發送多少個請求後會重啓該線程,咱們須要適當下降這個值,用以讓php-fpm自動的釋放內存,不是大部分網上說的51200等等,實際上還有另外一個跟它有關聯的值max_children,這個是每次php-fpm會創建多少個進程,這樣實際上的內存消耗是max_children*max_requests*每一個請求使用內存,根據這個咱們能夠預估一下內存的使用狀況,就不用再寫腳本去kill了。
request_terminate_timeout = 30;
最大執行時間, 在php.ini中也能夠進行配置(max_execution_time)
request_slowlog_timeout = 2; 開啓慢日誌
slowlog = log/$pool.log.slow; 慢日誌路徑
rlimit_files = 1024; 增長php-fpm打開文件描述符的限制
3.php-fpm的高CPU使用率排查方法
1)使用top命令, 直接執行top命令後,輸入1就能夠看到各個核心的CPU使用率。並且經過top -d 0.1能夠縮短採樣時間
2)查詢php-fpm慢日誌
grep -v "^$" www.log.slow.tmp | cut -d " " -f 3,2 | sort | uniq -c | sort -k1,1nr | head -n 50
5181 run() /www/test.net/framework/web/filters/CFilter.php:41 5156 filter() /www/test.net/framework/web/filters/CFilterChain.php:131 2670 = /www/test.net/index.php 2636 run() /www/test.net/application/controllers/survey/index.php:665 2630 action() /www/test.net/application/controllers/survey/index.php:18 2625 run() /www/test.net/framework/web/actions/CAction.php:75 2605 runWithParams() /www/test.net/framework/web/CController.php:309 2604 runAction() /www/test.net/framework/web/filters/CFilterChain.php:134 2538 run() /www/test.net/framework/web/CController.php:292 2484 runActionWithFilters() /www/test.net/framework/web/CController.php:266 2251 run() /www/test.net/framework/web/CWebApplication.php:276 1799 translate() /www/test.net/application/libraries/Limesurvey_lang.php:118 1786 load_tables() /www/test.net/application/third_party/php-gettext/gettext.php:254 1447 runController() /www/test.net/framework/web/CWebApplication.php:135
參數解釋:
sort: 對單詞進行排序
uniq -c: 顯示惟一的行,並在每行行首加上本行在文件中出現的次數
sort -k1,1nr: 按照第一個字段,數值排序,且爲逆序
head -10: 取前10行數據
3)用strace跟蹤進程
a)利用nohup將strace轉爲後臺執行,直到attach上的php-fpm進程死掉爲止:
nohup strace -T -p 13167 > 13167-strace.log &
參數說明:
-c 統計每一系統調用的所執行的時間,次數和出錯的次數等.
-d 輸出strace關於標準錯誤的調試信息.
-f 跟蹤由fork調用所產生的子進程.
-o filename,則全部進程的跟蹤結果輸出到相應的filename
-F 嘗試跟蹤vfork調用.在-f時,vfork不被跟蹤.
-h 輸出簡要的幫助信息.
-i 輸出系統調用的入口指針.
-q 禁止輸出關於脫離的消息.
-r 打印出相對時間關於,,每個系統調用.
-t 在輸出中的每一行前加上時間信息.
-tt 在輸出中的每一行前加上時間信息,微秒級.
-ttt 微秒級輸出,以秒了表示時間.
-T 顯示每一調用所耗的時間.
-v 輸出全部的系統調用.一些調用關於環境變量,狀態,輸入輸出等調用因爲使用頻繁,默認不輸出.
-V 輸出strace的版本信息.
-x 以十六進制形式輸出非標準字符串
-xx 全部字符串以十六進制形式輸出.
-a column
設置返回值的輸出位置.默認爲40.
-e execve 只記錄 execve 這類系統調用
-p 主進程號
b)用利用-c參數讓strace幫助彙總,很是方便很是強大!
[root@b28-12 log]# strace -cp 9907 Process 9907 attached - interrupt to quit Process 9907 detached % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 56.61 0.016612 5 3121 read 11.11 0.003259 1 2517 715 stat 8.04 0.002358 7 349 brk 6.02 0.001767 1 1315 poll 4.28 0.001255 6 228 recvfrom 2.71 0.000796 1 671 open 2.54 0.000745 0 2453 fcntl 2.37 0.000696 1 1141 write 1.69 0.000497 1 593 13 access 1.37 0.000403 0 1816 lseek 0.89 0.000262 1 451 22 sendto 0.56 0.000163 1 276 208 lstat 0.49 0.000145 0 384 getcwd 0.31 0.000090 0 1222 fstat 0.28 0.000082 0 173 munmap 0.26 0.000077 0 174 mmap 0.24 0.000069 2 41 socket 0.23 0.000068 0 725 close 0.00 0.000000 0 13 rt_sigaction 0.00 0.000000 0 13 rt_sigprocmask 0.00 0.000000 0 1 rt_sigreturn 0.00 0.000000 0 78 setitimer 0.00 0.000000 0 26 26 connect 0.00 0.000000 0 15 2 accept 0.00 0.000000 0 39 recvmsg 0.00 0.000000 0 26 shutdown 0.00 0.000000 0 13 bind 0.00 0.000000 0 13 getsockname 0.00 0.000000 0 65 setsockopt 0.00 0.000000 0 13 getsockopt 0.00 0.000000 0 8 getdents 0.00 0.000000 0 26 chdir 0.00 0.000000 0 1 futex ------ ----------- ----------- --------- --------- ---------------- 100.00 0.029344 18000 986 total
4.使用Opcode緩存(http://www.cnblogs.com/JohnABC/p/4531038.html)
5.對PHP性能進行監控
xdebug.auto_trace = on
xdebug.auto_profile = on
xdebug.collect_params = on
xdebug.collect_return = on
xdebug.profiler_enable = on
xdebug.trace_output_dir = "/tmp"
xdebug.profiler_output_dir ="/tmp"
6.監測php-fpm線程狀態
nginx配置
location ~ ^/status$ { include fastcgi_params; fastcgi_pass 127.0.0.1:9000; fastcgi_param SCRIPT_FILENAME $fastcgi_script_name; }
php-fpm配置
pm.status_path = /status
這樣的話經過http://域名/status就能夠看到當前的php狀況
下面介紹每一個參數的做用:
pool:php-fpm池的名稱,通常都是應該是www
process manage:進程的管理方法,php-fpm支持三種管理方法,分別是static,dynamic和ondemand,通常狀況下都是dynamic
start time:php-fpm啓動時候的時間,不論是restart或者reload都會更新這裏的時間
start since:php-fpm自啓動起來通過的時間,默認爲秒
accepted conn:當前接收的鏈接數
listen queue:在隊列中等待鏈接的請求個數,若是這個數字爲非0,那麼最好增長進程的fpm個數
max listen queue:從fpm啓動以來,在隊列中等待鏈接請求的最大值
listen queue len:等待鏈接的套接字隊列大小
idle processes:空閒的進程個數
active processes:活動的進程個數
total processes:總共的進程個數
max active processes:從fpm啓動以來,活動進程的最大個數,若是這個值小於當前的max_children,能夠調小此值
max children reached:當pm嘗試啓動更多的進程,卻由於max_children的限制,沒有啓動更多進程的次數。若是這個值非0,那麼能夠適當增長fpm的進程數
slow requests:慢請求的次數,通常若是這個值未非0,那麼可能會有慢的php進程,通常一個很差的mysql查詢是最大的禍首。
7.開啓php-fpm慢日誌
slowlog = /usr/local/php/log/php-fpm.log.slow
request_slowlog_timeout = 5s
8.設置php-fpm單次請求最大執行時間,今天碰到一個問題,測試服務器php-fpm一直是被佔滿狀態,後來發現是set_time_limit(0),file_get_content(),緣由以下:
好比file_get_contents(url)等函數,若是網站反應慢,會一直等在那兒不超時,php-fpm一直被佔用。有一個參數 max_execution_time 能夠設置 PHP 腳本的最大執行時間,可是,在 php-cgi(php-fpm) 中,該參數不會起效。真正可以控制 PHP 腳本最大執行時間的是 php-fpm.conf 配置文件中的如下參數。
request_terminate_timeout = 10s
默認值爲 0 秒,也就是說,PHP 腳本會一直執行下去。這樣,當全部的 php-cgi 進程都卡在 file_get_contents() 函數時,這臺 Nginx+PHP 的 WebServer 已經沒法再處理新的 PHP 請求了,Nginx 將給用戶返回「502 Bad Gateway」。可使用 request_terminate_timeout = 30s,可是若是發生 file_get_contents() 獲取網頁內容較慢的狀況,這就意味着 150 個 php-cgi 進程,每秒鐘只能處理 5 個請求,WebServer 一樣很難避免「502 Bad Gateway」。php-cgi進程數不夠用、php執行時間長、或者是php-cgi進程死掉,都會出現502錯誤。
要作到完全解決,只能讓 PHP 程序員們改掉直接使用 file_get_contents("http://example.com/") 的習慣,而是稍微修改一下,加個超時時間,用如下方式來實現 HTTP GET 請求。要是以爲麻煩,能夠自行將如下代碼封裝成一個函數。
<?php $ctx = stream_context_create(array( 'http' => array( 'timeout' => 1 //設置一個超時時間,單位爲秒 ) ) ); file_get_contents("http://example.com/", 0, $ctx);
固然,致使 php-cgi 進程 CPU 100% 的緣由不僅有這一種,那麼,怎麼肯定是 file_get_contents() 函數致使的呢?
首先,使用 top 命令查看 CPU 使用率較高的 php-cgi 進程。
top - 10:34:18 up 724 days, 21:01, 3 users, load average: 17.86, 11.16, 7.69 Tasks: 561 total, 15 running, 546 sleeping, 0 stopped, 0 zombie Cpu(s): 5.9%us, 4.2%sy, 0.0%ni, 89.4%id, 0.2%wa, 0.0%hi, 0.2%si, 0.0%st Mem: 8100996k total, 4320108k used, 3780888k free, 772572k buffers Swap: 8193108k total, 50776k used, 8142332k free, 412088k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 10747 www 18 0 360m 22m 12m R 100.6 0.3 0:02.60 php-cgi 10709 www 16 0 359m 28m 17m R 96.8 0.4 0:11.34 php-cgi 10745 www 18 0 360m 24m 14m R 94.8 0.3 0:39.51 php-cgi 10707 www 18 0 360m 25m 14m S 77.4 0.3 0:33.48 php-cgi 10782 www 20 0 360m 26m 15m R 75.5 0.3 0:10.93 php-cgi 10708 www 25 0 360m 22m 12m R 69.7 0.3 0:45.16 php-cgi 10683 www 25 0 362m 28m 15m R 54.2 0.4 0:32.65 php-cgi 10711 www 25 0 360m 25m 15m R 52.2 0.3 0:44.25 php-cgi 10688 www 25 0 359m 25m 15m R 38.7 0.3 0:10.44 php-cgi 10719 www 25 0 360m 26m 16m R 7.7 0.3 0:40.59 php-cgi
找其中一個 CPU 100% 的 php-cgi 進程的 PID,用如下命令跟蹤一下:
strace -p 10747
若是屏幕顯示:
select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0}) poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout) select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0}) poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout) select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0}) poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout) select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0}) poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout) select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0}) poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout) select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0}) poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout) select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0}) poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout) select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0}) poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout) select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0}) poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout) select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0}) poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout)
那麼,就能夠肯定是 file_get_contents() 致使的問題了。(參考:http://zyan.cc/tags/request_terminate_timeout/1/)
9.查看php-fpm啓動時間(能夠得出執行了多長時間)
ps -A -o pid,lstart,cmd |grep php-fpm
10.【待完善】