1. 程式人生 > 其它 >Nginx重試機制,瀏覽器重複請求兩次多次

Nginx重試機制,瀏覽器重複請求兩次多次

場景還原

問題 使用者再瀏覽器裡執行了一次http請求,結果後端伺服器執行了兩遍,如果這次請求是Insert操作,可想而知,資料庫會多出一條一模一樣的記錄來。

  • 閘道器用Nginx做了反向代理和負載均衡,Nginx下掛著兩臺阿里雲ECS伺服器,每臺機器上都裝著Tomcat,使用者開啟瀏覽器,點選頁面,訪問後端介面,檢視Nginx的access.log,結果這一條請求打在了兩臺伺服器上。

問題剖析

nginx的重試機制就是容錯的一種,在nginx的配置檔案中,proxy_next_upstream項定義了什麼情況下進行重試,官網文件中給出的說明如下:

Syntax: proxy_next_upstream error | timeout | invalid_header | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | off 
Default:    proxy_next_upstream error timeout;
Context:    http, server, location
  • 預設情況下,當請求伺服器發生錯誤或超時時,會嘗試到下一臺伺服器。
  • 問題找到了,原因是Nginx配置檔案中,超時時間太短了:proxy_connect_timeout 20;;在Nginx的預設配置是:在客戶端請求伺服器超時的情況下,Nginx會自動轉發該請求到另外一臺伺服器上,這是Nginx的一種容錯機制,所以Nginx的訪問日誌中會出現同一條請求而兩臺伺服器都執行了一遍的情況,這樣以來,程式如果沒有做冪等性操作的話資料庫會出現兩條記錄。
  • 還有一個引數影響了重試的次數:proxy_next_upstream_tries,官方文件中給出的說明如下:
  Syntax: proxy_next_upstream_tries number;
    Default:    proxy_next_upstream_tries 0;
    Context:    http, server, location
    This directive appeared in version 1.7.5.

調整

本來就是Nginx的一種容錯機制,這種機制在查詢操作還是挺好的,如果是插入操作,那就有點問題了,如果這條插入的請求特別耗時,並且時間超過Nginx的proxy_connect_timeout時間設定,Nginx會自動將該請求轉發叢集中的另外一臺伺服器的。但是我們不能將這種機制關閉,關閉以後會影響Nginx效率的,那怎麼辦哪?於是想出了一個臨時解決方案,專門針對耗時時間長的幾個介面做一下過濾,也就是說,在Nginx的server配置標籤中,專門對幾個特定的url過過濾,關閉Nginx的重試機制,配置如下

server {
       location ~ /api/insertData {
              proxy_connect_timeout 60;
              proxy_send_timeout 60;
              proxy_read_timeout 60;
              proxy_next_upstream off;

        }
 }

也可以直接關閉重試機制

proxy_next_upstream off;

nginx timeout 配置 全域性timeout 區域性timeout web timeout

nginx比較強大,可以針對單個域名請求做出單個連線超時的配置.

比如些動態解釋和靜態解釋可以根據業務的需求配置

proxy_connect_timeout :後端伺服器連線的超時時間_發起握手等候響應超時時間

proxy_read_timeout:連線成功後_等候後端伺服器響應時間_其實已經進入後端的排隊之中等候處理(也可以說是後端伺服器處理請求的時間)

proxy_send_timeout :後端伺服器資料回傳時間_就是在規定時間之內後端伺服器必須傳完所有的資料

nginx使用proxy模組時,預設的讀取超時時間是60s。

1、請求超時

http {
    include       mime.types;
    server_names_hash_bucket_size  512;     
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;  #保持
    tcp_nodelay on;
    client_header_timeout 15;
    client_body_timeout 15;
    send_timeout 25;
    include vhosts/*.conf;
}

2、後端伺服器處理請求的時間設定(頁面等待伺服器響應時間)

location / {
        ...
        proxy_read_timeout 150;  # 秒
        ...
    }

nginx常用的超時配置說明---timeout引數

1. 常規timeout引數

send_timeout

語法:send_timeout time

預設值:60s

配置區域:http server location

說明:設定響應傳輸到客戶端的超時時間。僅在兩個連續的寫操作之間設定超時,而不是為整個
響應的傳輸。如果客戶端在此時間內未收到任何內容,則會關閉連線。

client_header_timeout

語法:client_header_timeout time

預設值:60s

配置區域:http server

說明:指定等待client傳送一個請求頭的超時時間(例如:GET / HTTP/1.1).僅當在一次read中,沒有收到請求頭,才會算成超時。如果在超時時間內,client沒傳送任何東西,nginx返回HTTP狀態碼408(“Request timed out”)

client_body_timeout

語法:client_body_timeout time

預設值:60s

配置區域:http server location

說明:該指令設定請求體(request body)的讀超時時間。僅當在一次readstep中,沒有得到請求體,就會設為超時。超時後,nginx返回HTTP狀態碼408(“Request timed out”)

keepalive_timeout

語法:keepalive_timeout timeout [ header_timeout ]

預設值:75s

配置區域:http server location

說明:第一個引數指定了與client的keep-alive連線超時時間。伺服器將會在這個時間後關閉連線。可選的第二個引數指定了在響應頭Keep-Alive: timeout=time中的time值。這個頭能夠讓一些瀏覽器主動關閉連線,這樣伺服器就不必要去關閉連線了。沒有這個引數,nginx不會發送Keep-Alive響應頭(儘管並不是由這個頭來決定連線是否“keep-alive”)
兩個引數的值可並不相同

  • 注意不同瀏覽器怎麼處理“keep-alive”頭
  • MSIE和Opera忽略掉"Keep-Alive: timeout=" header.
  • MSIE保持連線大約60-65秒,然後傳送TCP RST
  • Opera永久保持長連線
  • Mozilla keeps the connection alive for N plus about 1-10 seconds.
  • Konqueror保持長連線N秒

lingering_timeout

語法:lingering_timeout time

預設值:5s

配置區域:http server location

說明:lingering_close生效後,在關閉連線前,會檢測是否有使用者傳送的資料到達伺服器,如果超過lingering_timeout時間後還沒有資料可讀,就直接關閉連線;否則,必須在讀取完連線緩衝區上的資料並丟棄掉後才會關閉連線。

resolver_timeout

語法:resolver_timeout time

預設值:30s

配置區域:http server location

說明:該指令設定DNS解析超時時間

2. fastcgi的timeout設定

配置區域:http

fastcgi_connect_timeout

fastcgi連線超時時間,預設60秒

fastcgi_send_timeout

nginx程序向fastcgi程序傳送請求過程的超時時間,預設值60秒

fastcgi_read_timeout

fastcgi程序向nginx程序傳送輸出過程的超時時間,預設值60秒

3. proxy轉發模組的timeout設定

proxy_connect_timeout

語法:proxy_connect_timeout time

預設值:60s

配置區域:http server location

說明:該指令設定與upstream server的連線超時時間,有必要記住,這個超時不能超過75秒。

這個不是等待後端返回頁面的時間,那是由proxy_read_timeout宣告的。如果你的upstream伺服器起來了,但是hanging住了(例如,沒有足夠的執行緒處理請求,所以把你的請求放到請求池裡稍後處理),那麼這個宣告是沒有用的,由於與upstream伺服器的連線已經建立了。

proxy_read_timeout

語法:proxy_read_timeout time

預設值:60s

配置區域:http server location

說明:該指令設定與代理伺服器的讀超時時間。它決定了nginx會等待多長時間來獲得請求的響應。這個時間不是獲得整個response的時間,而是兩次reading操作的時間。

proxy_send_timeout

語法:proxy_send_timeout time

預設值:60s

配置區域:http server location

說明:這個指定設定了傳送請求給upstream伺服器的超時時間。超時設定不是為了整個傳送期間,而是在兩次write操作期間。如果超時後,upstream沒有收到新的資料,nginx會關閉連線

proxy_upstream_fail_timeout(fail_timeout)

語法:server address [fail_timeout=30s]

預設值:10s

配置區域:upstream

說明:Upstream模組下 server指令的引數,設定了某一個upstream後端失敗了指定次數(max_fails)後,該後端不可操作的時間,預設為10秒

4. 負載均衡配置timeout時的2個引數

  • fail_timeout:預設10s
  • max_fails:預設1次

這2個引數一起配合,來控制在fail_timeout的時間內nginx怎樣認為upstream中的某個server是失效的,某個server連線失敗了max_fails次,則nginx會認為該server不工作了。同時,在接下來的 fail_timeout時間內,nginx不再將請求分發給失效的server。如果不設定這2個引數,fail_timeout預設為10s,max_fails預設為1。就是說,只要某個server失效一次,則在接下來的10s內,就不會分發請求到該server上

調整快取區大小

有時因為http請求和響應的資料量比較大,導致超出nginx預設快取而返回504這類情況,可以適當調整各類緩衝區大小來滿足實際需求:

fastcgi_buffers 8 128k;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_buffer_size 4k;
proxy_buffers 32 4k;
proxy_busy_buffers_size 64k;