使用 nginx 負載平衡伺服器重複 POST 請求(狀態 499)
雙重上傳
自從我們從一個簡單的 Apache 實例轉變為負載均衡環境後,有時會出現重複 POST 請求的問題。我們將 nginx 作為反向代理執行。靜態內容由 nginx 本身提供,動態內容由兩個 Apache 後端提供。
我檢查過它不是界面/使用者錯誤。一個小例子:一個簡單的圖像上傳將導致圖像被上傳兩次。請求/POST 不會通過點兩下或使用者失敗發送兩次。我沒有發現任何證據表明瀏覽器發送了兩次請求,所以我懷疑是在伺服器端。(請注意,這只是懷疑。)這些請求大部分是內部的,這意味著它們來自員工,所以我可以驗證它們是如何產生的。
我能找到的唯一“錯誤”是 nginx
499
在這些情況下會記錄錯誤。但是,我不確定這是問題的原因還是(副作用)。(我知道 499 不是預設的 http 狀態,它是 nginx 狀態,意思是“客戶端已關閉連接”)要求
重複的 POST 請求幾乎是所有可能需要一段時間的請求。我在這裡展示的一個例子是一個簡單的圖像上傳,但腳本在後台做了一些事情(圖像必須轉換成不同的格式/大小,並且應該分發到兩個伺服器等)。
日誌
一個例子是上傳圖片。nginx 將記錄一個“499”和一個 200 請求,但 Apache 正在接收(並處理!)兩個請求。
阿帕奇
[17:17:37 +0200] "POST ***URL** HTTP/1. 0" 200 9045 [17:17:47 +0200] "POST ***URL** HTTP/1. 0" 200 20687
nginx
[17:17:47 +0200] "POST ***URL** HTTP/1.1" 499 0 [17:17:52 +0200] "POST ***URL** HTTP/1.1" 200 5641
懷疑
在我看來,更大/更慢的上傳更受此影響,所以我懷疑超時。我試圖閱讀 499 錯誤:結論似乎是“客戶端關閉連接”。這可能是後台的情況,但我不確定這意味著應該發出第二個請求,並且肯定不會發生“使用者關閉瀏覽器”之類的事情。
目前,它似乎有助於分解較慢的 POST 請求(如果有很多事情要做,只需讓使用者選擇 1 並為另一個選擇第二次 POST),但這可能只是降低了它發生的機會。沒有把握。
這顯然是一個臨時解決方案。如果是超時,我需要找出在哪裡並增加相應的數字,但我不確定為什麼超時會導致這種行為:我懷疑是“嗯,出錯了”消息,而不是重複。
問題
我正在尋找哪些過程/情況會導致重複發布 POST。當然,任何“不確定為什麼,但可以通過增加此超時時間來解決”也很好。
nginx 配置
NGINX.conf
user nginx; worker_processes 2; worker_rlimit_nofile 10240; error_log /var/log/nginx/error.log error; pid /var/run/nginx.pid; events { multi_accept on; worker_connections 4096; use epoll; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nodelay off; client_max_body_size 30m; keepalive_timeout 65; include /etc/nginx/conf.d/*.conf; }
conf.d
我已經刪除了
geo
元件中的一些特定於 IP 的行以及SSL
變體,以保持簡單。如果需要,我可以替換它們,但歸結geo
為 ssl 後端以及相應的上游和 conf 文件的額外部分。geo $backend { default apache-backend; } upstream apache-backend { ip_hash; server SERVER1 max_fails=3 fail_timeout=30s weight=2; server SERVER2 max_fails=3 fail_timeout=30s weight=3; }
conf.d/somestring.conf
limit_conn_zone $binary_remote_addr zone=somestring:10m; server { listen ip1:80; listen ip2:80; server_name name.tld www.name.tld; root PATH access_log PATH/name.log main; location / { proxy_pass http://$backend; } //*some more locations**// gzip on; gzip_http_version 1.0; gzip_comp_level 2; gzip_proxied any; gzip_min_length 1100; gzip_buffers 16 8k; gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript; }
conf.d/proxy.conf
proxy_set_header Accept-Encoding ""; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_buffering on; proxy_read_timeout 90; proxy_buffer_size 32k; proxy_buffers 8 32k; proxy_busy_buffers_size 32k; proxy_temp_file_write_size 32k;
從這個論壇主題中,我們了解到罪魁禍首可能是 SPDY。對於那個使用者來說,禁用它似乎是一個解決方案,而且自從禁用它以來,我們也沒有發布過雙重文章。
確切的問題,除了“SPDY 做到了”,目前尚不清楚,建議的解決方案(禁用 SPDY)的副作用顯然是“不再有 SPDY”,但我們可以忍受。
在錯誤再次出現之前,我將其稱為“修復”(或至少:問題的解決方案)。
編輯:我們還沒有(25-02-2014)看到這個問題出現了,所以這確實是一個持久的解決方案。
簡短的回答:試試這個你的位置塊:
location / { proxy_read_timeout 120; proxy_next_upstream error; proxy_pass http://$backend; }
更長的解釋:
我想我剛剛遇到了你描述的問題:
- 我使用 nginx 反向代理作為負載均衡器
- 對於長時間執行的請求,後端會多次收到相同的請求
- 上游節點的 nginx 訪問日誌顯示
499
這些請求的狀態,並且相同的請求出現在不同的上游節點事實證明,這實際上是 nginx 作為反向代理的預設行為,因此將其升級到更高版本將無法解決此問題,儘管此處給出了可能的解決方案,但這解決了另一個問題。
發生這種情況是因為 nginx 作為負載均衡器以循環方式選擇上游節點。當選擇的節點失敗時,請求被發送到下一個節點。這裡要注意的重要一點是節點故障預設歸類為
error or timeout
. 由於您沒有設置 aproxy_read_timeout
,因此預設值為 60 秒。所以在等待 60 秒後,nginx 會選擇下一個節點並發送相同的請求。因此,一種解決方案是增加此超時,以便您可以完成長時間執行的操作,例如通過設置
proxy_read_timeout 120;
(或任何適合您需要的限制)。另一種選擇是阻止反向代理嘗試使用下一個節點,以防超時,通過設置
proxy_next_upstream error;
. 或者,您可以按照上面的建議設置這兩個選項。