web-dev-qa-db-ja.com

POSTリクエストはnginxロードバランスサーバーで繰り返されます(ステータス499)

二重アップロード

単純なApacheインスタンスから負荷分散環境に移行して以来、POSTリクエストが繰り返されるという問題が発生することがあります。nginxをリバースプロキシとして実行しています。静的コンテンツはnginx自体によって提供されています。動的コンテンツは2つのApacheバックエンドから提供されます。

インターフェース/ユーザーのエラーではないことを確認しました。簡単な例:単純な画像アップロードでは、画像が2回アップロードされます。リクエスト/ POSTは、ダブルクリックまたはユーザーエラーによって2回送信されることはありません。ブラウザーがリクエストを2回送信しているという証拠を見つけられなかったので、私の疑いはサーバー側にあります。 (これは疑いのみであることに注意してください。)これらの要求のほとんどは内部的なものであり、つまり従業員からのものであるため、どのようにして発生するかを確認できます。

私が見つけることができる唯一の「間違った」ことは、nginxが499これらの場合のエラー。ただし、これが問題の原因なのか、それとも単なる(副次的な)影響なのかはわかりません。 (私は499がデフォルトのhttpステータスではないことを知っています、それは「クライアントが接続を閉じた」ことを意味するnginxステータスです)

リクエスト

繰り返されるPOST要求は、時間がかかる可能性のあるほとんどすべての要求です。ここで例として示しているのは、単純な画像のアップロードですが、スクリプトはバックグラウンドでいくつかの処理を実行します(画像は異なるフォーマット/サイズに変換する必要があり、両方のサーバーに配布する必要があります)。

ログ

たとえば、画像のアップロードです。 nginxは1つの「499」と1つの200リクエストをログに記録しますが、Apacheは2つのリクエストを受信(および処理)しています。

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エラーを読み込もうとしました。結論は、「クライアントが接続を閉じた」ということです。それはバックグラウンドのケースかもしれませんが、これがどのようにして2番目のリクエストが発行されるべきであるか、そして「ユーザーがブラウザを閉じた」のようなものが起こっていないことは確かではありません。

現在、より遅いPOSTリクエストを分割するのに役立つようです(複数の処理がある場合は、ユーザーに1を選択させ、POSTもう1つ)、しかし、これはそれが発生する可能性を単に低くしている可能性があります。

これは明らかに一時的な解決策です。それがisタイムアウトの場合、どこを見つけて対応する数値を増やす必要がありますが、なぜタイムアウトがこの動作を引き起こすのかはわかりません。 "メッセージではなく、繰り返し。

ご質問

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の一部だけでなく、SSLの一部のIP固有の行も削除して、単純にしました。必要に応じてそれらを置き換えることができますが、結局、sslバックエンド用の追加のgeo部分、および対応するアップストリームと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;
8
Nanne

このフォーラムのトピック から、カルプリントがSPDYである可能性があることがわかりました。そのユーザーにとってそれを無効にする解決策のようであり、それを無効にして以来、二重の投稿はありませんでした。

「SPDYがそれをした」以外の正確な問題は現時点では不明であり、提案されたソリューション(SPDYを無効にする)の副作用は明らかに「これ以上SPDY」ではありませんが、それでも問題ありません。

バグが再び発生するまで、これを「修正」(または少なくとも:問題の解決策)と呼びます。

編集:この問題が発生するのを(25-02-2014)確認していないので、これは確かに永続的な解決策のようです。

1
Nanne

短い答え:ロケーションブロックにこれを試してください:

location / {
  proxy_read_timeout 120;
  proxy_next_upstream error;
  proxy_pass http://$backend;
}

より長い説明:

私はちょうどあなたが説明した問題にちょうど遭遇したと思います:

  • Nginxリバースプロキシをロードバランサーとして使用しています
  • 長時間実行されるリクエストの場合、バックエンドは同じリクエストを複数回受信します
  • 上流ノードのnginxアクセスログはこれらのリクエストの499ステータスを示し、同じリクエストが異なる上流ノードに表示されます

これは実際にはnginxのリバースプロキシとしてのデフォルトの動作であり、それをより高いバージョンにアップグレードしてもこの問題は解決されませんが、可能な解決策として示されています here が、これは別のアドレスです問題。

これは、ロードバランサーとしてのnginxが上流ノードを ラウンドロビン方式 で選択するために発生します。選択したノードが失敗すると、要求は次のノードに送信されます。ここで注意すべき重要な点は、ノード障害はデフォルトで error or timeout として分類されることです。 proxy_read_timeoutを設定していないため、デフォルトは60秒です。したがって、60秒間待機した後、nginxは次のノードを選択し、同じリクエストを送信します。

したがって、1つの解決策は、このタイムアウトを増やして、長時間実行オペレーションを完了できるようにすることです。 proxy_read_timeout 120;(またはニーズに合った制限)を設定します。

もう1つのオプションは、proxy_next_upstream error;を設定して、タイムアウトの場合にリバースプロキシが次のノードを使用しないようにすることです。または、上記のように、これらのオプションを両方とも設定できます。

4
pymkin