天天看點

Nginx code 常用狀态碼學習小結

最近了解下Nginx的Code狀态碼,在此簡單總結下。一個http請求處理流程:

Nginx code 常用狀态碼學習小結

一個普通的http請求處理流程,如上圖所示:

A -> client端發起請求給nginx

B -> nginx處理後,将請求轉發到uwsgi,并等待結果

C -> uwsgi處理完請求後,傳回資料給nginx

D -> nginx将處理結果傳回給用戶端

每個階段都會有一個預設的逾時時間,由于網絡、機器負載、代碼異常等等各種原因,如果某個階段沒有在預期的時間内正常傳回,就會導緻這次請求異常,進而産生不同的狀态碼。

1)504 錯誤

504主要是針對B、C階段。一般nginx配置中會有:

location / {
    ...
    uwsgi_connect_timeout 6s;
    uwsgi_send_timeout 6s;
    uwsgi_read_timeout 10s; 
    uwsgi_buffering on;
    uwsgi_buffers 80 16k; 
    ...
}

這個代表nginx與上遊伺服器(uwsgi)通信的逾時時間,也就是說,如果在這個時間内,uwsgi沒有響應,則認為這次請求逾時,傳回504狀态碼。

具體的日志如下:

access_log

[16/May/2016:22:11:38 +0800] 10.4.31.56 201605162211280100040310561523 15231401463407888908 10.*.*.* 127.0.0.1:8500 "GET /api/media_article_list/?count=10&source_type=0&status=all&from_time=0&item_id=0&flag=2&_=1463407896337 HTTP/1.1" 504 **.***.com **.**.**.39, **.**.**.60 10.000 10.000 "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.71 Safari/537.36" ...
error_log

2016/05/16 22:11:38 [error] 90674#0: *947302032 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 10.6.19.81, server: **.***.com, request: "GET /api/media_article_list/?count=10&source_type=0&status=all&from_time=0&item_id=0&flag=2&_=1463407896337 HTTP/1.1", upstream: "http://127.0.0.1:8500/**/**/api/media_article_list/?count=10&source_type=0&status=all&from_time=0&item_id=0&flag=2&_=1463407896337", host: "mp.toutiao.com", referrer: "https://**.***.com/articles/?source_type=0"
error_log中upstream timed out (110: Connection timed out) while reading response header from upstream,

意思是說,在規定的時間内,沒有從header中拿到資料,即uwsgi沒有傳回任何資料。
      

Nginx 504 Gateway Time-out的含義是所請求的網關沒有請求到,簡單來說就是沒有請求到可以執行的PHP-CGI。Nginx 504 Gateway Time-out則是與nginx.conf的設定有關。504 Gateway Time-out問題常見于使用nginx作為web server的伺服器的網站。

一般看來, 這種情況可能是由于nginx預設的fastcgi程序響應的緩沖區太小造成的, 這将導緻fastcgi程序被挂起, 如果你的fastcgi服務對這個挂起處理的不好, 那麼最後就極有可能導緻504 Gateway Time-out現在的網站, 尤其某些論壇有大量的回複和很多内容的, 一個頁面甚至有幾百K預設的fastcgi程序響應的緩沖區是8K, 我們可以設定大點在nginx.conf裡, 加入:

fastcgi_buffers 8 128k
這表示設定fastcgi緩沖區為8×128k,當然如果您在進行某一項即時的操作, 可能需要nginx的逾時參數調大點, 例如設定成60秒:

send_timeout 60;

隻是調整了上面這兩個參數, 結果可能就是沒有再顯示那個逾時!

解決辦法:調整nginx.conf的相關設定:
fastcgi_connect_timeout 600;
fastcgi_send_timeout 600;
fastcgi_read_timeout 600;
fastcgi_buffer_size 256k;
fastcgi_buffers 16 256k;
fastcgi_busy_buffers_size 512k;
fastcgi_temp_file_write_size 512k;       

2)502 錯誤

502主要針對B 、C階段。産生502的時候,對應的error_log中的内容會有好幾種:

access_log

[16/May/2016:16:39:49 +0800] 10.4.31.56 201605161639490100040310562612 2612221463387989972 10.6.19.81 127.0.0.1:88 "GET /articles/?source_type=0 HTTP/1.1" 503 **.***.com **.**.**.4, **.**.**.160 0.000 0.000 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36" "uuid=\x22w:546d345b86ca443eb44bd9bb1120e821\x22; tt_webid=15660522398; lasttag=news_culture; sessionid=f172028cc8310ba7f503adb5957eb3ea; sid_tt=f172028cc8310ba7f503adb5957eb3ea; _ga=GA1.2.354066248.1463056713; _gat=1"      

error_log

2016/05/16 16:39:49 [error] 90693#0: *944980723 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 10.6.19.80, server: **.***.com, request: "GET /articles/ HTTP/1.1", upstream: "http://127.0.0.1:8500/**/**/articles/", host: "**.***.com", referrer: "http://**.***.com/new_article/"      

列一下常見的幾種502對應的 error_log:

  • recv() failed (104: Connection reset by peer) while reading response header from upstream
  • upstream prematurely closed connection while reading response header from upstream
  • connect() failed (111: Connection refused) while connecting to upstream
  • ....

這些都代表,在nginx設定的逾時時間内,上遊uwsgi沒有給正确的響應(但是是有響應的,不然如果一直沒響應,就會變成504逾時了),是以nginx這邊的狀态碼為502。

如上,access_log中出現503,為什麼?

這個是因為nginx upstream的容災機制。如果nginx有如下配置:

upstream app_backup {
    server 127.0.0.1:8500 max_fails=3 fail_timeout=5s;
    server 127.0.0.1:88 backup;
}
      
  • max_fails=3 說明嘗試3次後,會認為“ server 127.0.0.1:8500” 失效,于是進入 “server 127.0.0.1:88 backup”,即通路本機的88端口;
  • nginx upstream的容災機制,預設情況下,Nginx 預設判斷失敗節點狀态以connect refuse和time out狀态為準,不過location裡加了這個配置:
    proxy_next_upstream error http_502;                                                                                                                  
    proxy_connect_timeout 1s; 
    proxy_send_timeout    6s; 
    proxy_read_timeout    10s;
    proxy_set_header Host $host;      
  • 這個配置是說,對于http狀态是502的情況,也會走upstream的容災機制;
  • 概括一下就是,如果連續有3次(max_fails=3)狀态為502的請求,則會任務這個後端server 127.0.0.1:8500 挂掉了,在接下來的5s(fail_timeout=5s)内,就會通路backup,即server 127.0.0.1:88 ,看下88端口對應的是什麼:
    server {                                                                                                                                             
        listen 88;
        access_log /var/log/nginx/failover.log;
        expires 1m;
        error_page  500 502 503 504 /500.html;
        location / {
           return 503; 
        }
        location = /500.html {
           root /**/**/**/nginx/5xx/;
        }
    }      

這個的意思就是,對于通路88端口的請求,nginx會傳回503狀态碼,同時傳回/opt/tiger/ss_conf/nginx/5xx/這個路徑下的500.html檔案。

是以,access_log中看到的是503。

Nginx 502 Bad Gateway的含義是請求的PHP-CGI已經執行,但是由于某種原因(一般是讀取資源的問題)沒有執行完畢而導緻PHP-CGI程序終止。Nginx 502錯誤的原因比較多,一般就是因為在代理模式下後端伺服器出現問題引起的。這些錯誤一般都不是nginx本身的問題,一定要從後端找原因!比如:php-cgi程序數不夠用、php執行時間長、或者是php-cgi程序死掉,都會出現502錯誤。502錯誤最通常的出現情況就是後端主機當機!!

一般來說Nginx 502 Bad Gateway和php-fpm.conf的設定有關,php-fpm.conf有兩個至關重要的參數,一個是"max_children",另一個是"request_terminate_timeout" ,但是這個值不是通用的,而是需要自己計算的。

計算的方式如下:

1)request_terminate_timeout

如果伺服器性能足夠好,且寬帶資源足夠充足,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這個錯誤。

2)max_children

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這個錯誤。

=================502的解決辦法================

一般解決辦法

遇到502問題,可以優先考慮按照以下兩個步驟去解決。
1)檢視目前的PHP FastCGI程序數是否夠用:)
netstat -anpo | grep "php-cgi" | wc -l

如果實際使用的“FastCGI程序數”接近預設的“FastCGI程序數”,那麼,說明“FastCGI程序數”不夠用,需要增大。
# ps aux | grep php-cgi |wc -l
130

# netstat -anpo | grep "php-cgi" | wc -l
450

# netstat -anpo | grep "php-cgi" |more
tcp        0      0 192.168.12.201:52719        192.168.12.203:13002        ESTABLISHED 27687/php-cgi       off (0.00/0/0)
tcp        0      0 192.168.12.201:52713        192.168.12.203:13002        ESTABLISHED 27685/php-cgi       off (0.00/0/0)
tcp        0      0 192.168.12.201:52694        192.168.12.203:13002        ESTABLISHED 27682/php-cgi       off (0.00/0/0)
tcp        0      0 192.168.12.201:52688        192.168.12.203:13002        ESTABLISHED 27681/php-cgi       off (0.00/0/0)
tcp        0      0 192.168.12.201:52701        192.168.12.203:13002        ESTABLISHED 27683/php-cgi       off (0.00/0/0)

重新開機php服務後
# netstat -anpo | grep "php-cgi" | wc -l
46

2)部分PHP程式的執行時間超過了Nginx的等待時間,可以适當增加nginx.conf配置檔案中FastCGI的timeout時間,例如:
...... http { ...... fastcgi_connect_timeout 300; fastcgi_send_timeout 300; fastcgi_read_timeout 300; ...... } ......

php.ini中memory_limit設低了會出錯,修改了php.ini的memory_limit為128M,重新開機nginx,發現好了,原來是PHP的記憶體不足了。
      

臨時解決辦法

Nginx提示502和504錯誤的臨時解決辦法是:調整php-fpm.conf的相關設定:
<value name=”max_children”>32</value>
<value name=”request_terminate_timeout”>30s</value>  fast-cgi的執行腳本時間
      

終級解決方案

以上解決方案隻能臨時解決問題,而如果網站的通路量确實非常非常大,而Nginx+FastCGI隻能對處理瞬間或短時間内的高并發有很好的效果,
是以目前唯一的終極解決方案是:定時平滑重新開機php-cgi。

1)寫一個非常簡單的腳本:
# vim /home/www/scripts/php-fpm.sh
#!/bin/bash
# This script run at */1
/usr/local/php/sbin/php-fpm reload

2)将腳本添加至計劃任務:)
# crontab -e
*/1 * * * * /home/www/scripts/php-fpm.sh

為了省事起見,也可以不寫腳本,直接在crontab裡寫入php-fpm的平滑重新開機指令。
      

Nginx 502錯誤情況

1))網站的通路量大,而php-cgi的程序數偏少。
針對這種情況的502錯誤,隻需增加php-cgi的程序數。具體就是修改/usr/local/php/etc/php-fpm.conf 檔案,将其中的max_children值适當增加。
這個資料要依據你的VPS或獨立伺服器的配置進行設定。一般一個php-cgi程序占20M記憶體,你可以自己計算下,适量增多。
/usr/local/php/sbin/php-fpm restart 然後重新開機一下.

2))CPU占用率、記憶體占用率非常高,遭到CC攻擊.
解決方法請參考:Linux VPS簡單解決CC攻擊

3)CPU占用率不高,記憶體溢出。
檢查一下網站程式有沒有問題?一般小偷站點常常會出現記憶體溢出。
檢查一下/var/log/目錄下的日志,看看是不是有人爆破SSH和FTP端口?
SSH、FTP遭到窮舉也會占用大量記憶體。是的話改掉SSH端口和FTP端口即可。      

3)499錯誤

client發送請求後,如果在規定的時間内(假設逾時時間為500ms)沒有拿到nginx給的響應,則認為這次請求逾時,會主動結束,這個時候nginx的access_log就會列印499狀态碼。

A+B+C+D > 500ms

其實這個時候,server端有可能還在處理請求,隻不過client斷掉了連接配接,是以處理結果也無法傳回給用戶端。

499如果比較多的話,可能會引起服務雪崩。

比如說,client一直在發起請求,用戶端因為某些原因處理慢了,沒有在規定時間内傳回資料,client認為請求失敗,中斷這次請求,然後再重新發起請求。這樣不斷的重複,服務端的請求越來越多,機器負載變大,請求處理越來越慢,沒有辦法響應任何請求

官網總結nginx傳回499的情況,是由于:

client has closed connection    #用戶端主動關閉了連接配接。
      

client has closed connection    #用戶端主動關閉了連接配接。

client has closed connection    #用戶端主動關閉了連接配接。
      

解決的話,可以添加

proxy_ignore_client_abort    on;
      

還有一種原因,确實是用戶端關閉了連接配接,或者連接配接逾時。主要是因為PHP程序數太少,或php程序占用,資源不能很快釋放,請求堆積。這種情況要解決的話,需要在程式上做優化。

499報錯即是用戶端關閉連接配接,這個狀态碼并不是http協定中定義的status code,而是nginx自己定義的一個狀态碼。由于伺服器處理請求較多,用戶端在有效時間内沒有得到答複,主動關閉了連接配接。有人說把時間設定長一些或者使用proxy_ignore_client_abort on(讓代理服務端不要主動關閉用戶端的連接配接)。但是這樣也有一定的風險,會拖垮伺服器。發生這個錯誤,如果伺服器CPU和記憶體不算太高,一般是資料庫和程式的問題,資料庫處理較慢或者程式線程較低。結合情況調整,比如讀寫分離或者程式線程數調高。

4)500錯誤

伺服器内部錯誤,也就是伺服器遇到意外情況,而無法執行請求。發生錯誤,一般的幾種情況:

  • web腳本錯誤,如php文法錯誤,lua文法錯誤等。
  • 通路量大的時候,由于系統資源限制,而不能打開過多的檔案句柄

分析錯誤的原因

  • 檢視nginx,php的錯誤日志
  •  如果是too many open files,修改nginx的worker_rlimit_nofile參數,使用ulimit檢視系統打開檔案限制,修改/etc/security/limits.conf
  • 如果腳本存在問題,則需要修複腳本錯誤,并優化代碼
  • 各種優化都做好,還是出現too many open files,那就需要考慮做負載均衡,把流量分散到不同伺服器上去

5)503錯誤

503是服務不可用的傳回狀态。

由于在nginx配置中,設定了limit_req的流量限制,導緻許多請求傳回503錯誤代碼,在限流的條件下,為提高使用者體驗,希望傳回正常Code 200,且傳回操作頻繁的資訊:

location  /test {  
  ...   
  limit_req zone=zone_ip_rm burst=1 nodelay;   
  error_page 503 =200 /dealwith_503?callback=$arg_callback;  
}  
location /dealwith_503{   
  set $ret_body '{"code": "V00006","msg": "操作太頻繁了,請坐下來喝杯茶。"}';  
   if ( $arg_callback != "" )   
   {   
       return 200 'try{$arg_callback($ret_body)}catch(e){}';   
   }   
   return 200 $ret_body;   
}  
      

6)400錯誤:request header or cookie too large

解決辦法:
修改nginx.conf,添加下面内容(即增加緩沖區)
[root@fvtlb01 ~]# vim /data/nginx/conf/nginx.conf
......
http
{
......
client_header_buffer_size 8k;                #預設是4k(可以稍微改大,比如16K)
large_client_header_buffers 4 8k;

......

}
      

=================nginx日志"110: Connection timed out"報錯=================

通路nginx頁面,出現5xx報錯。檢視nginx日志,發現如下報錯資訊:
upstream timed out (110: Connection timed out) while reading response header from upstream

解決辦法:修改fastcgi_read_timeout的時間值,預設是60s,比如可以改成600s或3000s      

................................................Nginx Code Status...............................

200:伺服器成功傳回網頁 
403:伺服器拒絕請求。
404:請求的網頁不存在 
499:用戶端主動斷開了連接配接。
500:伺服器遇到錯誤,無法完成請求。
502:伺服器作為網關或代理,從上遊伺服器收到無效響應。
503 - 服務不可用 
504:伺服器作為網關或代理,但是沒有及時從上遊伺服器收到請求。
這些狀态碼被分為五大類: 
100-199 用于指定用戶端應相應的某些動作。 
200-299 用于表示請求成功。 
300-399 用于已經移動的檔案并且常被包含在定位頭資訊中指定新的位址資訊。 
400-499 用于指出用戶端的錯誤。 (自己電腦這邊的問題) 自己電腦這邊的問題) 
500-599 用于支援伺服器錯誤。 (對方的問題) 對方的問題)
---------------------------------------------------------------------------------------------
200 (成功) 伺服器已成功處理了請求。 通常,這表示伺服器提供了請求的網頁。 
201 (已建立) 請求成功并且伺服器建立了新的資源。 
202 (已接受) 伺服器已接受請求,但尚未處理。 
203 (非授權資訊) 伺服器已成功處理了請求,但傳回的資訊可能來自另一來源。 
204 (無内容) 伺服器成功處理了請求,但沒有傳回任何内容。 
205 (重置内容) 伺服器成功處理了請求,但沒有傳回任何内容。
206 (部分内容) 伺服器成功處理了部分 GET 請求。 
---------------------------------------------------------------------------------------------
300 (多種選擇) 針對請求,伺服器可執行多種操作。 伺服器可根據請求者 (user agent) 選擇一項操作,或提供操作清單供請求者選擇。 
301 (永久移動) 請求的網頁已永久移動到新位置。 伺服器傳回此響應(對 GET 或 HEAD 請求的響應)時,會自動将請求者轉到新位置。
302 (臨時移動) 伺服器目前從不同位置的網頁響應請求,但請求者應繼續使用原有位置來進行以後的請求。
303 (檢視其他位置) 請求者應當對不同的位置使用單獨的 GET 請求來檢索響應時,伺服器傳回此代碼。
304 (未修改) 自從上次請求後,請求的網頁未修改過。 伺服器傳回此響應時,不會傳回網頁内容。 
305 (使用代理) 請求者隻能使用代理通路請求的網頁。 如果伺服器傳回此響應,還表示請求者應使用代理。 
307 (臨時重定向) 伺服器目前從不同位置的網頁響應請求,但請求者應繼續使用原有位置來進行以後的請求。 
---------------------------------------------------------------------------------------------
400 (錯誤請求) 伺服器不了解請求的文法。 
401 (未授權) 請求要求身份驗證。 對于需要登入的網頁,伺服器可能傳回此響應。 
403 (禁止) 伺服器拒絕請求。
404 (未找到) 伺服器找不到請求的網頁。
405 (方法禁用) 禁用請求中指定的方法。 
406 (不接受) 無法使用請求的内容特性響應請求的網頁。 
407 (需要代理授權) 此狀态代碼與 401(未授權)類似,但指定請求者應當授權使用代理。
408 (請求逾時) 伺服器等候請求時發生逾時。 
409 (沖突) 伺服器在完成請求時發生沖突。 伺服器必須在響應中包含有關沖突的資訊。 
410 (已删除) 如果請求的資源已永久删除,伺服器就會傳回此響應。 
411 (需要有效長度) 伺服器不接受不含有效内容長度标頭字段的請求。 
412 (未滿足前提條件) 伺服器未滿足請求者在請求中設定的其中一個前提條件。 
413 (請求實體過大) 伺服器無法處理請求,因為請求實體過大,超出伺服器的處理能力。 
414 (請求的 URI 過長) 請求的 URI(通常為網址)過長,伺服器無法處理。 
415 (不支援的媒體類型) 請求的格式不受請求頁面的支援。 
416 (請求範圍不符合要求) 如果頁面無法提供請求的範圍,則伺服器會傳回此狀态代碼。 
417 (未滿足期望值) 伺服器未滿足"期望"請求标頭字段的要求。 
---------------------------------------------------------------------------------------------
500 (伺服器内部錯誤) 伺服器遇到錯誤,無法完成請求。 
501 (尚未實施) 伺服器不具備完成請求的功能。 例如,伺服器無法識别請求方法時可能會傳回此代碼。 
502 (錯誤網關) 伺服器作為網關或代理,從上遊伺服器收到無效響應。 
503 (服務不可用) 伺服器目前無法使用(由于超載或停機維護)。 通常,這隻是暫時狀态。 
504 (網關逾時) 伺服器作為網關或代理,但是沒有及時從上遊伺服器收到請求。 
505 (HTTP 版本不受支援) 伺服器不支援請求中所用的 HTTP 協定版本。      

proxy_intercept_errors

當上遊伺服器響應頭回來後,可以根據響應狀态碼的值進行攔截錯誤處理,與error_page 指令互相結合。用在通路上遊伺服器出現錯誤的情況下。

如下的一個配置執行個體:

[root@dev ~]# cat ssl-zp.wangshibo.conf
upstream mianshi1 {
server 192.168.1.33:8080 max_fails=3 fail_timeout=10s;
#server 192.168.1.32:8080 max_fails=3 fail_timeout=10s;
}
 
 
server {
listen 443;
server_name zp.wangshibo.com;
ssl on;
 
### SSL log files ###
access_log logs/zrx_access.log;
error_log logs/zrx_error.log;
 
### SSL cert files ###
ssl_certificate ssl/wangshibo.cer;
ssl_certificate_key ssl/wangshibo.key;
ssl_session_timeout 5m;
 
error_page 404 301 https://zp.wangshibo.com/zrx-web/;
 
location /zrx-web/ {
proxy_pass http://mianshi1;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
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_set_header X-Forwarded-Proto https;
#proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
proxy_intercept_errors on;
}
}      

*************** 當你發現自己的才華撐不起野心時,就請安靜下來學習吧!***************