PHP-php-fpm配置優化

前言: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的性能監控功能,將xdebug輸出結果經過WinCacheGrind軟件分析。
  xdebug的安裝和配合IDE調試的方法參見: Vim+XDebug調試PHP

  php.ini中配置的這幾項是輸出性能信息的:
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"

  這樣XDebug會輸出全部執行php函數的性能數據,但產生的文件也會比較大。能夠關閉一些選項如collect_params、collect_return,
  來減小輸出的數據量。或者關閉自動輸出,經過在想要監控的函數首尾調用xdebug函數來監控指定的函數。

  輸出的文件名相似cachegrind.out.1277560600和trace.3495983249.txt,能夠拿到Windows平臺下用WinCacheGrind進行圖形化分析。

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.【待完善】

相關文章
相關標籤/搜索