背景:2020/07/06 02:58:19 [error] 31066#0: *3921 upstream timed out (110: Connection timed out) while reading response header from upstream, client: XXXX, server: localhost, request: "GET / HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "XXXXX"
Nginx報上述錯誤,被折磨得半死,網上一堆的博客,你抄我,我抄你,大部分都是知其然而不知其所以然,總結自己找到的有效方法原理,也歡迎大家批評指正;
個人技術架構:nginx+php/反向代理+mysql
目錄
1 有關於超時的原因
拋開所有軟件所在操作系統性能參數不談,有關於超時能出現的地方總結來說可以分爲5類:
- nginx面向客戶端;
- nginx面向後端,如面向php,負載均衡的二級服務器設備等;
- php處理;
- php面向MySQL;
- MySQL處理;
通過上面5類我們可以明顯發現,當超時問題出現的時候,我們要排查的是整個系統哪個過程出了問題,而不是單純地只改某一個地方!!!所以造成了網上很多資料按照操作來沒有效果。
2 Nginx調優
Nginx調優通過上面的邏輯,我們只要做好(1)(2)就可以了.
2.1 面向客戶端的請求超時
這個是在nginx.conf的http模塊配置,配置如下:
http {
…
keepalive_timeout 65; #保持
tcp_nodelay on;
client_header_timeout 15;
client_body_timeout 15;
send_timeout 25;
…
}
client_header_timeout
默認值 60s,上下文 http server(指可以放在http塊和server塊)
指定等待client發送一個請求頭的超時時間(例如:GET / HTTP/1.1).僅當在一次read中,沒有收到請求頭,纔會算成超時。如果在超時時間內,client沒發送任何東西,nginx返回HTTP狀態碼408(“Request timed out”)
client_body_timeout
默認值 60,上下文 http server location
該指令設置請求體(request body)的讀超時時間。僅當在一次readstep中,沒有得到請求體,就會設爲超時。超時後,nginx返回HTTP狀態碼408(“Request timed out”)
keepalive_timeout (長連接類型)
KeepAlive 模式,它告訴 webserver 在處理完一個請求後保持這個 TCP 連接的打開狀態。若接收到來自客戶端的其它請求,服務端會利用這個未被關閉的連接,而不需要再建立一個連接。
KeepAlive 在一段時間內保持打開狀態,它們會在這段時間內佔用資源。佔用過多就會影響性能。
Nginx 使用 keepalive_timeout 來指定 KeepAlive 的超時時間(timeout)。指定每個 TCP 連接最多可以保持多長時間。Nginx 的默認值是 75 秒,有些瀏覽器最多隻保持 60 秒,所以可以設定爲 60 秒。若將它設置爲 0,就禁止了 keepalive 連接。
lingering_timeout
默認值 5s
上下文 http server location
說明 lingering_close生效後,在關閉連接前,會檢測是否有用戶發送的數據到達服務器,如果超過lingering_timeout時間後還沒有數據可讀,就直接關閉連接;否則,必須在讀取完連接緩衝區上的數據並丟棄掉後纔會關閉連接。
resolver_timeout
默認值 30s
上下文 http server location
說明 該指令設置DNS解析超時時間
2.2 面向連接方式的超時配置
這裏就要解釋nginx與php的連接方式了,一種是訪問本機的php-fpm,一種是反向代理的方式。主要配置在server配置節之內。
2.3.1 php-fpm的方式
原理:Nginx -> FastCGI -> php-fpm -> FastCGI Wrapper -> php解析器
CGI是通用網關協議,FastCGI則是一種常駐進程的CGI模式程序。我們所熟知的PHP-FPM的全稱是PHP FastCGI Process Manager,即PHP-FPM會通過用戶配置來管理一批FastCGI進程,例如在PHP-FPM管理下的某個FastCGI進程掛了,PHP-FPM會根據用戶配置來看是否要重啓補全,PHP-FPM更像是管理器,而真正銜接Nginx與PHP的則是FastCGI進程。
圖中,FastCGI的下游CGI-APP就是PHP程序。而FastCGI的上游是Nginx,他們之間有一個通信載體,即圖中的socket。在我們上文圖3的配置文件中,fastcgi_pass所配置的內容,便是告訴Nginx你接收到用戶請求以後,你該往哪裏轉發,在我們圖3中是轉發到本機的一個socket文件,這裏fastcgi_pass也常配置爲一個http接口地址(這個可以在php-fpm.conf中配置)。而上圖5中的Pre-fork,則對應着我們PHP-FPM的啓動,也就是在我們啓動PHP-FPM時便會根據用戶配置啓動諸多FastCGI觸發器(FastCGI Wrapper)
PHP提供SAPI面向Webserver來提供擴展編程。但是這樣的方式意味着你要是自主研發一套Webserver,你就需要學習SAPI,並且在你的Webserver程序中實現它。這意味着你的Webserver與PHP產生了耦合。解決耦合的辦法:CGI協議,比較好的方式是有一套通用的規範,上下游都兼容它。那麼CGI協議便成了Nginx、PHP都願意接受的一種方式,而FastCGI常住進程的模式又讓上下游程序有了高併發的可能。
連接方式:unix socket和tcp socket
unix socket
需要在nginx配置文件中填寫php-fpm運行的pid文件地址。
location ~ \.php$ {
fastcgi_pass unix:/var/run/php-fpm.sock;#根據你的路徑來
}
tcp socket
需要在nginx配置文件中填寫php-fpm運行的ip地址和端口號。
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
}
弄懂了連接方式,那麼server的配置方式如下:
server {
listen 80;
server_name localhost;
fastcgi_connect_timeout 300;
fastcgi_read_timeout 300;
fastcgi_send_timeout 300;
fastcgi_buffer_size 64k;
fastcgi_buffers 4 32k;
fastcgi_busy_buffers_size 64k;
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
}
}
從上面的配置中我們可以發現,緩存大小的設置對性能也是有影響的!
2.3.2 反向代理方式
這種就是你的請求轉發給後端服務器的方式,反向代理不做過多介紹,具體例子如下:
server {
listen 80;
server_name localhost;
large_client_header_buffers 4 16k; # 讀取大型客戶端請求頭的緩衝區的最大數量和大小
client_max_body_size 300m; #設置nginx能處理的最大請求主體大小。
client_body_buffer_size 128k; #請求主體的緩衝區大小。
proxy_connect_timeout 600;
proxy_read_timeout 600;
proxy_send_timeout 600;
proxy_buffer_size 64k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
……
}
提高nginx網絡吞吐量buffers優化指令說明
large_client_header_buffers
此指令規定了用於讀取大型客戶端請求頭的緩衝區的最大數量和大小。 這些緩衝區僅在缺省緩衝區不足時按需分配。 當處理請求或連接轉換到保持活動狀態時,釋放緩衝區。
client_max_body_size
此指令設置NGINX能處理的最大請求主體大小。 如果請求大於指定的大小,則NGINX發回HTTP 413(Request Entity too large)錯誤。 如果服務器處理大文件上傳,則該指令非常重要。
client_body_buffer_size
此指令設置用於請求主體的緩衝區大小。 如果主體超過緩衝區大小,則完整主體或其一部分將寫入臨時文件。 如果NGINX配置爲使用文件而不是內存緩衝區,則該指令會被忽略。 默認情況下,該指令爲32位系統設置一個8k緩衝區,爲64位系統設置一個16k緩衝區。 該指令在NGINX配置的http,server和location區塊使用。
nginx代理超時配置
proxy_connect_timeout
默認值60s, nginx連接到後端服務器的連接超時時間,發起握手等候響應超時時間。
proxy_read_timeout
連接成功後,等候後端服務器響應時間。其實已經進入後端的排隊之中等候處理(也可以說是後端服務器處理請求的時間)。
proxy_send_timeout
後端服務器數據回傳時間,就是在規定時間之內後端服務器必須傳完所有的數據。
注:nginx使用proxy模塊時,默認的讀取超時時間是60s。
nginx緩存區大小設置
proxy_buffer_size
nginx可從服務器一次接收的最大數據大小
proxy_buffers
該指令設置緩衝區的大小和數量,從被代理的後端服務器取得的響應內容,會放置到這裏. 默認情況下,一個緩衝區的大小等於內存頁面大小,可能是4K也可能是8K,這取決於平臺。
proxy_busy_buffers_size
proxy_busy_buffers_size不是獨立的空間,他是proxy_buffers和proxy_buffer_size的一部分。nginx會在沒有完全讀完後端響應的時候就開始向客戶端傳送數據,所以它會劃出一部分緩衝區來專門向客戶端傳送數據(這部分的大小是由proxy_busy_buffers_size來控制的,建議爲proxy_buffers中單個緩衝區大小的2倍),然後它繼續從後端取數據,緩衝區滿了之後就寫到磁盤的臨時文件中。
proxy_max_temp_file_size和proxy_temp_file_write_size
臨時文件由proxy_max_temp_file_size和proxy_temp_file_write_size這兩個指令決定。
proxy_temp_file_write_size 是一次訪問能寫入的臨時文件的大小,默認是proxy_buffer_size和proxy_buffers中設置的緩衝區大小的2倍,Linux下一般是8k。
proxy_max_temp_file_size 指定當響應內容大於proxy_buffers指定的緩衝區時, 寫入硬盤的臨時文件的大小. 如果超過了這個值, Nginx將與Proxy服務器同步的傳遞內容, 而不再緩衝到硬盤. 設置爲0時, 則直接關閉硬盤緩衝.
3 php-fpm調優
3.1 用戶組問題
這個問題比較欺負菜鳥,當然,老玩家也會因爲一時大意,未配置號權限造成訪問超時的問題。在php-fpm.conf中配置:
listen.owner = nobody
listen.group = nobody
user = nobody
group = nobody
這裏最好與nginx的用戶一樣,有的人會問,爲啥要配置成nobody?我偏不行不行?行,你想叫啥就叫啥,爲了安全最好不要配置成高權限的用戶,如root。
3.2 php-fpm.conf調優
pm = static | dynamic | ondemand
pm.max_children = 15
pm.start_servers = 5
pm.min_spare_servers = 5 //空閒時間最小的php-fpm進程
pm.max_spare_servers = 5 //空閒時間最大的php-fpm進程
pm.max_requests = 1024
request_terminate_timeout = 0
pm = static | dynamic | ondemand
static模式下php-fpm子進程數量固定;dynamic就是動態分配
動態適合小內存機器,靈活分配進程,省內存。靜態適用於大內存機器,動態創建回收進程對服務器資源也是一種消耗。
如果你的內存很大,有8-20G,按照一個php-fpm進程20M算,100個就2G內存了,那就可以開啓static模式。如果你的內存很小,比如才256M,那就要小心設置了,因爲你的機器裏面的其他的進程也算需要佔用內存的,所以設置成dynamic是最好的,比如:pm.max_chindren = 8, 佔用內存160M左右,而且可以隨時變化,對於一半訪問量的網站足夠了。
pm.max_children
最大可創建的子進程的數量。看你機子情況了,設置大了沒意義。
一般來說一臺服務器正常情況下每一個php-cgi所耗費的內存在20M~30M左右,因此看你機座的具體情況。
如果長時間沒有得到處理的請求就會出現 504 Gateway Time-out 這個錯誤,而正在處理的很累的那幾個php-cgi如果遇到了問題就會出現 502 Bad gateway 這個錯誤。
pm.start_servers
隨着php-fpm一起啓動時創建的子進程數目。默認值:min_spare_servers + (max_spare_servers - min_spare_servers) / 2。
pm.min_spare_servers
設置服務器空閒時最小php-fpm進程數量。必須設置。如果空閒的時候,會檢查如果少於10個,就會啓動幾個來補上。
pm.max_spare_servers
設置服務器空閒時最大php-fpm進程數量。必須設置。如果空閒時,會檢查進程數,多於30個了,就會關閉幾個,達到30個的狀態。
pm.max_requests
最大處理請求數是指一個php-fpm的worker進程在處理多少個請求後就終止掉,master進程會重新respawn新的。該配置可以避免php解釋器自身或程序引起的memory leaks。上面的配置最大請求數:15*1024=15360,最小請求數:5*1024=7120。
request_terminate_timeout
表示將執行時間太長的進程直接終止。這個也要根據自己機子實際情況來,設置爲0的時候表示請求時間將沒有超時限制。爲了防止php有循環bug,最好設置一個值。默認單位s,默認值0.一般設置10分鐘左右應該差不多了。
4 MySQL優化
MySQL優化是個大課題,針對我目前得現狀我只在數據庫內部改了interactive_timeout與wait_timeout,其它的大家可以一起討論一下。
interactive_timeout 參數含義:服務器關閉交互式連接前等待活動的秒數。交互式客戶端定義爲在mysql_real_connect()中使用CLIENT_INTERACTIVE選項的客戶端。 參數默認值:28800秒(8小時);
wait_timeout: 參數含義:服務器關閉非交互連接之前等待活動的秒數。 在線程啓動時,根據全局wait_timeout值或全局interactive_timeout值初始化會話wait_timeout值,取決於客戶端類型(由mysql_real_connect()的連接選項CLIENT_INTERACTIVE定義)。 參數默認值:28800秒(8小時);
這兩個參數決定一個持續SLEEP狀態的線程多久被關閉。如果你的MySQL Server有大量的閒置連接,他們不僅會白白消耗內存,而且如果連接一直在累加而不斷開,最終肯定會達到MySQL Server的連接上限數,這會報'too many connections'的錯誤。對於wait_timeout的值設定,應該根據系統的運行情況來判斷。在系統運行一段時間後,可以通過show processlist命令查看當前系統的連接狀態,如果發現有大量的sleep狀態的連接進程,則說明該參數設置的過大,可以進行適當的調整小些。
設置和查看:
mysql> show global variables like 'wait_timeout';
mysql> show global variables like 'interactive_timeout';
mysql> set global interactive_timeout=1800;
mysql> set global wait_timeout=1800;
5 某些技巧
看這類超時問題我的方法是逐一用jmeter壓測排查問題出現在哪裏,如果php運行時間過長還可以開啓慢日誌來尋找錯誤。在php-fpm下設置:
slowlog = /usr/local/var/log/php-fpm.log.slow
request_slowlog_timeout = 15s
當一個請求該設置的超時時間15秒後,就會將對應的PHP調用堆棧信息完整寫入到慢日誌中。php-fpm慢日誌會記錄下進程號,腳本名稱,具體哪個文件哪行代碼的哪個函數執行時間過長。
總結:有關於nginx+php+mysql優化一直是一個大問題,本人總結了目前我所能用到的一些情況,歡迎大家批評指正。
參考資料:
Nginx與PHP交互過程 + Nginx與PHP通信的兩種方式:https://blog.csdn.net/wuhuagu_wuhuaguo/article/details/83032578
nginx配置文件參數詳解:https://www.cnblogs.com/zclzhao/p/5033391.html
nginx中的超時設置,請求超時、響應等待超時等:https://www.cnblogs.com/lemon-flm/p/8352194.html
MySQL中interactive_timeout和wait_timeout的區別:https://www.cnblogs.com/ivictor/p/5979731.html
interactive_timeout和wait_timeout:https://www.cnblogs.com/zengkefu/p/5651910.html
Nginx+FastCGI到底是誰影響超時時間:http://www.linuxidc.com/Linux/2014-10/108012.htm
nginx響應超時upstream timed out (110: Connection timed out) while reading response header from upstream:https://blog.csdn.net/u014218983/article/details/81217032#nginx%E7%BC%93%E5%AD%98%E5%8C%BA%E5%A4%A7%E5%B0%8F%E8%AE%BE%E7%BD%AE