最近在工作中,需要做Excel匯入的功能,由於Excel的資料比較多,而且我們的服務端程式需要對資料的內容做校正,會調用很多的外部服務介面,所以毫無懸念的匯入Excel介面調用超過了一分鐘,並且報錯:504 gateway timeout。以下是兩種解決思路:
1. 最佳化業務代碼
一個介面調用超過一分鐘,一定有可以最佳化的地方,看看資料庫或者介面的調用是否合理,是否可以合并請求。
2. 修改Nginx的伺服器配置
如果實在是最佳化不了了,可以把Nginx的逾時時間上調。
看看時間是否符合要求,在nginx.config裡面的三個參數:
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
以上的單位是秒。
如果使用了Nginx的代理,可以在塊裡加上:
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
變成:
location /foo {
proxy_pass http://xxx.xxx.xxx.xxx:8080/foo;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
access_log /var/log/nginx/access.foo.log main;
error_log /var/log/nginx/error.foo.log;
}
如果沒有解決我們再來看看
從錯誤碼基本可以確定跟nginx本身無關,主要是提交給php-fpm的請求未能正確反饋而導致,一般情況下,提交動態請求的時候,nginx會直接把 請求轉交給php-fpm,而php-fpm再分配php-cgi進程來處理相關的請求,之後再依次返回,最後由nginx把結果反饋給用戶端瀏覽器,但 我這個vps目前跑的是個純php應用內容,實際上使用者所有的請求都是php請求,有的耗費時間比較久,php-cgi進程就一直都被用滿,而php- fpm本身的設定檔只開啟了10組php-cgi進程,這樣的話線上使用者稍微多的話就會導致請求無法被正常處理而出錯。
大概分析出了原 因,下面做就比較容易了,首先是更改php-fpm的幾處配置:
把max_children由之前的10改為現在的30,這樣就可以保證 有充足的php-cgi進程可以被使用;
把request_terminate_timeout由之前的0s改為60s,這樣php-cgi進程 處理指令碼的逾時時間就是60秒,可以防止進程都被掛起,提高利用效率。
接著再更改nginx的幾個配置項,減少FastCGI的請求次 數,盡量維持buffers不變:
fastcgi_buffers由 4 64k 改為 2 256k;
fastcgi_buffer_size 由 64k 改為 128K;
fastcgi_busy_buffers_size 由 128K 改為 256K;
fastcgi_temp_file_write_size 由 128K 改為 256K。
好了,重新載入php-fpm和nginx的配置,再次測試,至今兩周時間內沒有再出現504 Gateway Time-out的情況,算是達到效果了。
另外,php-fpm的預設靜態處理方式會使得php-cgi的進程長期佔用記憶體而無法釋放,這也是導致nginx出錯的原因之一,因此可以將php-fpm的處理方式改成apache模式。
<value name=”style”>apache-like</value>
從更改完畢到現在的測試表明上述方式的效果還是很明顯的,並沒有發現一次Nginx502 bad gateway或504 Gateway Time-out錯誤。當然,如果你的VPS或者伺服器的效能足夠好可以根據具體情況不必做無謂的改動。
執行個體
以我目前的伺服器為例子CPU是奔四1.5G的,記憶體1GB,CENTOS的系統,訪客大概是50人左右同時線上。
但是線上的人大都需要請求PHP-CGI進行大量的資訊處理,因此我將nginx.conf設定為:
fastcgi_connect_timeout 300s;
fastcgi_send_timeout 300s;
fastcgi_read_timeout 300s;
fastcgi_buffer_size 128k;
fastcgi_buffers 8 128k;#8 128
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_intercept_errors on;
這裡最主要的設定是前三條,即
fastcgi_connect_timeout 300s;
fastcgi_send_timeout 300s;
fastcgi_read_timeout 300s;
這裡規定了PHP-CGI的串連、發送和讀取的時間,300秒足夠用了,因此我的伺服器很少出現504 Gateway Time-out這個錯誤。最關鍵的是php-fpm.conf的設定,這個會直接導致502 Bad Gateway和504 Gateway Time-out。
下面我們來仔細分析一下php-fpm.conf幾個重要的參數:
php-fpm.conf有兩個至關重要的參數,一個是”max_children”,另一個是”request_terminate_timeout”
我的兩個設定的值一個是”40″,一個是”900″,但是這個值不是通用的,而是需要自己計算的。
計算的方式如下:
如果你的伺服器效能足夠好,且寬頻資源足夠充足,PHP指令碼沒有系迴圈或BUG的話你可以直接將”request_terminate_timeout”設定成0s。0s的含義是讓PHP-CGI一直執行下去而沒有時間限制。而如果你做不到這一點,也就是說你的PHP-CGI可能出現某個BUG,或者你的寬頻不夠充足或者其他的原因導致你的PHP-CGI能夠假死那麼就建議你給”request_terminate_timeout”賦一個值,這個值可以根據你伺服器的效能進行設定。一般來說效能越好你可以設定越高,20分鐘-30分鐘都可以。由於我的伺服器PHP指令碼需要長時間運行,有的可能會超過10分鐘因此我設定了900秒,這樣不會導致PHP-CGI死掉而出現502 Bad gateway這個錯誤。
而”max_children”這個值又是怎麼計算出來的呢?這個值原則上是越大越好,php-cgi的進程多了就會處理的很快,排隊的請求就會很少。設定”max_children”也需要根據伺服器的效能進行設定,一般來說一台伺服器正常情況下每一個php-cgi所耗費的記憶體在20M左右,因此我的”max_children”我設定成40個,20M*40=800M也就是說在峰值的時候所有PHP-CGI所耗記憶體在800M以內,低於我的有效記憶體1Gb。而如果我的”max_children”設定的較小,比如5-10個,那麼php-cgi就會“很累”,處理速度也很慢,等待的時間也較長。如果長時間沒有得到處理的請求就會出現504 Gateway Time-out這個錯誤,而正在處理的很累的那幾個php-cgi如果遇到了問題就會出現502 Bad gateway這個錯誤。
fastcgi的設定加在 server {}內.
改完後如果啟動nginx後提示:
nginx: [emerg] unknown directive " fastcgi_connect_timeout" in /home/chen/workspace/jamy/nginx.conf:2
類似這樣的錯誤,可能是沒有把前面的全形空格去掉。