nginx-php調優

一、一些常見的狀態碼爲:

  • 200 – 服務器成功返回網頁

  • 404(未找到) 服務器找不到請求的網頁。例如,對於服務器上不存在的網頁經常會返回此代碼。
  • 500(服務器內部錯誤) 服務器遇到錯誤,無法完成請求。而且一般程序上是ASP錯誤爲多的,可能是你的用戶權限的問題導致,或者是數據庫連接出現了錯誤
  • 502(錯誤網關) 服務器作爲網關或代理,從上游服務器收到無效響應。
  • 504(網關超時) 服務器作爲網關或代理,但是沒有及時從上游服務器收到請求。單個php-fpm進程阻塞超過nginx的時間閾值返回504 gateway timeout

二、分析
(1)502 Bad Gateway原因分析
將請求提交給網關如php-fpm執行,但是由於某些原因沒有執行完畢導致php-fpm進程終止執行。說到此,這個問題就很明瞭了,與網關服務如php-fpm的配置有關了。
php-fpm.conf配置文件中有兩個參數就需要你考慮到,分別是max_children和request_terminate_timeout。
max_children最大子進程數,在高併發請求下,達到php-fpm最大響應數,後續的請求就會出現502錯誤的。可以通過netstat命令來查看當前連接數。
設置max_children也需要根據服務器的性能進行設定,一般來說一臺服務器正常情況下每一個php-cgi所耗費的內存在20M左右。所以在峯值的時候,所有php-cgi所耗內存爲20 max_children數。
request_terminate_timeout設置單個請求的超時終止時間。還應該注意到php.ini中的max_execution_time參數。當請求終止時,也會出現502錯誤的。
當積累了大量的php請求,你重啓php-fpm釋放資源,但一兩分鐘不到,502又再次呈現,這是什麼原因導致的呢? 這時還應該考慮到數據庫,查看下數據庫進程是否有大量的locked進程,數據庫死鎖導致超時,前端終止了繼續請求,但是SQL語句還在等待釋放鎖,這時就要重啓數據庫服務了或kill掉死鎖SQL進程了。
I、Nginx錯誤訪問日誌:
2013/09/19 01:09:00 [error] 27600#0:
78887 recv() failed (104: Connection reset by peer) while reading response header from upstream
client: 192.168.1.101, server: test.com, request: "POST /index.php HTTP/1.1", upstream: "fastcgi://unix:/dev/shm/php-fcgi.sock:",
host: "test.com", referrer: "http://test.com/index.php"

II、PHP-FPM報錯日誌:
WARNING: child 25708 exited on signal 15 (SIGTERM) after 21008.883410 seconds from start

(2) 504 Gateway Time-out原因分析
504錯誤一般是與nginx.conf配置有關了。主要與以下幾個參數有關:fastcgi_connect_timeout、fastcgi_send_timeout、fastcgi_read_timeout、fastcgi_buffer_size、fastcgi_buffers、fastcgi_busy_buffers_size、fastcgi_temp_file_write_size、fastcgi_intercept_errors。特別是前三個超時時間。如果fastcgi緩衝區太小會導致fastcgi進程被掛起從而演變爲504錯誤。

三、nginx與php進程間通信
(1)FastCGI原理
FastCGI是一個運用於Http Server和動態腳本語言間通信的接口,多數流行的Http Server都支持FastCGI,包括Apache、Nginx和lighttpd等。同時,FastCGI也被許多腳本語言支持,其中就有PHP。
FastCGI接口方式採用C/S結構,可以將HttP服務器和腳本解析服務器分開,同時在腳本解析服務器上啓動一個或者多個腳本解析守護進程。當HttP服務器每次遇到動態程序時,可以將其直接交付給FastCGI進程來執行,然後將得到的結果返回給客戶端。這種方式可以讓HttP服務器專一地處理靜態請求或者將動態腳本服務器的結果返回給客戶端,這在很大程度上提高了整個應用系統的性能。
(2)Nginx+php-fpm實現原理
Nginx本身不會對PHP進行解析,終端對PHP頁面的請求將會被Nginx交給FastCGI進程監聽的IP地址及端口,由php-fpm作爲動態解析服務器處理,最後將處理結果再返回給nginx。其實,Nginx就是一個反向代理服務器。Nginx通過反向代理功能將動態請求轉向後端php-fpm,從而實現對PHP的解析支持,這就是Nginx實現PHP動態解析的原理。

Nginx不支持對外部程序的直接調用或者解析,所有的外部程序(包括PHP)必須通過FastCGI接口來調用。FastCGI接口在Linux下是socket(這個socket可以是文件socket,也可以是ip socket)。爲了調用CGI程序,還需要一個FastCGI的wrapper(wrapper可以理解爲用於啓動另一個程序的程序),這個wrapper綁定在某個固定socket上,如端口或者文件socket。當Nginx將CGI請求發送給這個socket的時候,通過FastCGI接口,wrapper接收到請求,然後派生出一個新的線程,這個線程調用解釋器或者外部程序處理腳本並讀取返回數據;接着,wrapper再將返回的數據通過FastCGI接口,沿着固定的socket傳遞給Nginx;最後,Nginx將返回的數據發送給客戶端。
    Nginx 簡單配置 

location ~ .php$ {
root /home/admin/web/nginx/html/;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /home/admin/web/nginx/html/$fastcgi_script_name;
include fastcgi_params;
}

當nginx接收到一個http請求時,通過配置文件找到對應的server。然後匹配server中的所有location,找到最匹配的。而在location中的命令會啓動不同的模塊去完成工作,比如rewrite模塊、index模塊。因此在nginx中模塊可以看作真正的勞動工作者。nginx的模塊是被編譯到nginx中的,屬於靜態方式。啓動nginx時,模塊被自動加載。不像apache,把模塊單獨編譯成so文件,在配置文件中指定是否加載。所以,單比模塊加載方面,nginx也比apache速度上有提升。
location ~ .php$ {
root /webpath;
fastcgi_pass 127.0.0.1:9000;

...
}

這個location指令把以php爲文件後綴的請求,交給127.0.0.1:9000處理。我想你看到這個應該猜到了,這是一個C/S架構東西。 而這裏的IP地址和端口(127.0.0.1:9000)就是fastcgi進程監聽的IP地址和端口。fastcgi的配置IP和端口從何而來呢?在php-fpm.conf中可以看到。
listen = 127.0.0.1:9000 #這個表示php的fastcgi進程監聽的ip地址以及端口
pm.start_servers = 2

php-fpm作爲fastcgi的進程管理器,可以有效控制內存和進程,並且平滑重載php配置。php5.3以後,php-fpm被集成到php的core中,默認安裝,無須配置。

fastcgi進程管理器php-fpm自身初始化,啓動主進程php-fpm和啓動start_servers個fastcgi子進程。主進程php-fpm主要是管理fastcgi子進程,監聽9000端口,fastcgi子進程等待請求。當客戶端請求到達nginx時,nginx通過location指令,將所有以php爲後綴的文件都交給 127.0.0.1:9000 來處理。php-fpm選擇並連接到一個fastcgi子進程,並將環境變量和標準輸入發送到fastcgi子進程。fastcgi子進程完成處理後將標準輸出和錯誤信息返回。當fastcgi子進程關閉連接時,請求便告處理完成,等待下次處理。
(3)fastcgi與cgi
I、cgi在2000年或更早的時候用得比較多, 以前web服務器一般只處理靜態的請求,如果碰到一個動態請求怎麼辦呢?web服務器會根據這次請求的內容,然後會fork一個新進程來運行外部c程序(或perl腳本...), 這個進程會把處理完的數據返回給web服務器,最後web服務器把內容發送給用戶,剛纔fork的進程也隨之退出。 如果下次用戶還請求改動態腳本,那麼web服務器又再次fork一個新進程,周而復始的進行。

後來出現了一種更高級的方式是, web服務器可以內置perl解釋器或php解釋器。 也就是說這些解釋器做成模塊的方式,web服務器會在啓動的時候就啓動這些解釋器。 當有新的動態請求進來時,web服務器就是自己解析這些perl或php腳本,省得重新fork一個進程,效率提高了。

II、fastcgi的方式是,web服務器收到一個請求時,他不會重新fork一個進程(因爲這個進程在web服務器啓動時就開啓了,而且不會退出),web服務器直接把內容傳遞給這個進程(進程間通信,但fastcgi使用了別的方式,tcp方式通信),這個進程收到請求後進行處理,把結果返回給web服務器,最後自己接着等待下一個請求的到來,而不是退出。

III、舉個例子: 服務端現在有個10萬個字單詞, 客戶每次會發來一個字符串,問以這個字符串爲前綴的單詞有多少個。 那麼可以寫一個程序,這個程序會建一棵trie樹,然後每次用戶請求過來時可以直接到這個trie去查找。 但是如果以cgi的方式的話,這次請求結束後這課trie也就沒了,等下次再啓動該進程時,又要新建一棵trie樹,這樣的效率就太低下了。 而用fastcgi的方式的話,這課trie樹在進程啓動時建立,以後就可以直接在trie樹上查詢指定的前綴了。

四、用戶對動態php網頁的訪問過程
(1)用戶瀏覽器發起對網頁的訪問:http://www.66rpg.com/index.php
(2)用戶和nginx服務器進行三次握手,進行tcp連接(忽略包括nginx訪問控制策略、防火牆等)
(3)第一步:用戶將http請求發送給nginx服務器
(4)第二步:nginx會根據用戶訪問的URL和後綴對請求進行判斷,例如用戶訪問的index.php則會根據配置文件中的location進行匹配。nginx根據用戶請求的資源匹配到具體的location後會執行location對應的動作。
fastcgi_params #表示nginx會調用fastcgi這個接口。
fastcgi_intercept_errors on; #表示開啓fastcgi的中斷和錯誤信息記錄。
fastcgi_pass 127.0.0.1::9000; #表示nginx通過fastcgi_pass將用戶請求的資源發給127.0.0.1:9000進行解析,這裏的nginx和php在同一臺服務器上,所以127.0.0.1:9000表示本地的php腳本解析服務器。
(5)第三步:通過第二步可以看出,用戶請求的是動態內容,nginx會將請求交給fastcgi客戶端,通過fastcgi_pass將用戶的請求發送給php-fpm。如果用戶訪問的是靜態資源,nginx直接將用戶請求的靜態資源返回給用戶
(6)第四步:fastcgi_pass將動態資源交給php-fpm之後,php-fpm會將資源轉給php腳本解析服務器的wrapper
(7)第五步:wrapper收到php-fpm轉過了的請求後,wrapper會生成一個新的線程調用php動態程序解析服務器。如果用戶請求的是需要讀取Mysql數據庫等,會出發讀庫操作;如果用戶請求的是圖片,附件等,php會觸發一次查詢後端存儲服務器的操作。
(8)第六步:php將查詢到的結果返回給nginx
(9)第七步:nginx構造一個響應報文將結果返回給用戶。

五、配置說明,調優
壓測:
合理的併發,且保證qps在一定範圍沒有明顯下降。
I、# vim /usr/local/php/etc/php-fpm.conf
pm.max_children = 800
pm.start_servers = 400
pm.min_spare_servers = 200
pm.max_spare_servers = 500
pm.max_requests = 2048

(1)php-fpm有一個參數 max_requests,該參數指明瞭,每個children最多處理多少個請求後便會被關閉,默認的設置是500。因爲php是把請求輪詢給每個children,在大流量下,每個childre到達max_requests所用的時間都差不多,這樣就造成所有的children基本上在同一時間被關閉,這樣會導致此時nginx發給php的請求無法得到相應,會出現短時間的502.解決方法:php-fpm有一個參數 max_requests,該參數指明瞭,每個children最多處理多少個請求後便會被關閉,默認的設置是500。

(2)php-fpm 進程池優化方法
php-fpm進程池開啓進程有兩種方式,一種是static,直接開啓指定數量的php-fpm進程,不再增加或者減少;
另一種則是dynamic,開始時開啓一定數量的php-fpm進程,當請求量變大時,動態的增加php-fpm進程數到上限,當空閒時自動釋放空閒的進程數到一個下限。
這兩種不同的執行方式,可以根據服務器的實際需求來進行調整。
要用到的一些參數,分別是pm、pm.max_children、pm.start_servers、pm.min_spare_servers和pm.max_spare_servers。
pm表示使用那種方式,有兩個值可以選擇,就是static(靜態)或者dynamic(動態)。
下面4個參數的意思分別爲:

pm.max_children:靜態方式下開啓的php-fpm進程數量,在動態方式下他限定php-fpm的最大進程數(這裏要注意pm.max_spare_servers的值只能小於等於pm.max_children)
pm.start_servers:動態方式下的起始php-fpm進程數量。
pm.min_spare_servers:動態方式空閒狀態下的最小php-fpm進程數量。
pm.max_spare_servers:動態方式空閒狀態下的最大php-fpm進程數量。

如果dm設置爲static,那麼其實只有pm.max_children這個參數生效。系統會開啓參數設置數量的php-fpm進程。
如果dm設置爲dynamic,4個參數都生效。系統會在php-fpm運行開始時啓動pm.start_servers個php-fpm進程,然後根據系統的需求動態在pm.min_spare_servers和pm.max_spare_servers之間調整php-fpm進程數。

PS.
pm.min_spare_servers、pm.max_spare_servers這2個參數一開始我以爲是指空閒進程,但是後來服務器給我報了一個錯誤:
pm.start_servers(70) must not be less than pm.min_spare_servers(15) and not greater than pm.max_spare_servers(60)
要求pm.start_servers的值在pm.min_spare_servers和pm.max_spare_servers之間,經過測試,得出上述結論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章