場景還原
問題 用戶再瀏覽器里執行了一次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;