使用nginx负载均衡服务器重复POST请求(状态499)

双重上传

自从我们从一个简单的Apache实例转到一个负载均衡的环境之后,有时POST请求会重复出现问题。 我们运行nginx作为反向代理。 静态内容由nginx自身提供,dynamic内容由两个Apache后端提供。

我检查了它不是一个接口/用户错误。 一个小例子:一个简单的图片上传将导致图片上传两次。 请求/ POST不会通过双击或用户失败发送两次。 我还没有find任何证据表明浏览器发送请求两次,所以我怀疑是在服务器端。 (请注意,这只是怀疑。)这些请求中的大多数是内部的,这意味着它们来自员工,所以我可以validation它们是如何发生的。

我唯一能find的错误是nginx会在这些情况下logging一个499错误。 但是,我不确定这是问题的原因还是(侧面)影响。 (我知道499不是默认的http状态,这是一个nginx状态,意思是“客户端已经closures了连接”)

要求

重复的POST请求几乎是所有可能需要一段时间的请求。 我在这里展示的一个例子是一个简单的图片上传,但脚本在后台做了一些东西(图片必须被转换成不同的格式/大小,并且应该分发到两个服务器等)。

日志

一个例子是上传图片。 nginx会logging一个'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错误:结论似乎是“客户端closures”。 在后台可能会出现这种情况,但我不确定这是否意味着应该发出第二个请求,并且确实没有像“用户closures的浏览器”这样的事情发生。

目前,它似乎有助于分解较慢的POST请求(如果有多个事情要做,只需要让用户select1,然后POST另一个),但这可能只是降低发生的机会。 不确定。

这显然是一个临时的解决scheme。 如果超时,我需要找出哪里,并增加相应的数字,但我不知道为什么超时会导致这种行为:我会怀疑一个“好,那错了”的消息,而不是重复。

问题

我正在查找哪些进程/情况可能会导致POST重复。 当然,任何“不知道为什么,但是通过增加这个超时值来解决”是很好的。

nginxconfiguration

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变化,以保持简单。 如果需要,我可以replace它们,但它归结为一个额外的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。 对于那个用户来说,它似乎是一个解决scheme,禁用它,我们还没有双重职位,因为禁用它。

确切的问题,除了“SPDY做到了”,在这一点上是未知的,所提出的解决scheme(禁用SPDY)的副作用显然是“不再SPDY”,但我们可以忍受。

直到错误再次出现,我把这称为“修复”(或至less:解决问题的办法)。

编辑:我们没有(25-02-2014)看到这个问题了,所以这似乎确实是一个持久的解决scheme。

简短的回答:试试这个你的位置块:

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

更长的解释:

我想我刚刚遇到了你所描述的问题:

  • 我使用nginx反向代理作为负载平衡器
  • 对于长时间运行的请求,后端会多次收到相同的请求
  • 上游节点的nginx访问日志显示499状态,相同的请求出现在不同的上游节点

事实certificate,这实际上是nginx作为反向代理的默认行为,升级到更高版本将不能解决这个问题,虽然它是作为一个可能的解决scheme在这里给出 ,但是这解决了一个不同的问题。

发生这种情况是因为nginx作为负载平衡器以循环方式select上游节点。 当select的节点失败时,请求被发送到下一个节点。 这里需要注意的是,节点故障默认分类为error or timeout 。 由于您没有设置proxy_read_timeout ,因此默认值为60秒。 所以在等待60秒之后,nginxselect下一个节点并发送相同的请求。

所以一个解决scheme是增加这个超时,以便您的长时间运行的操作可以完成,例如通过设置proxy_read_timeout 120; (或任何限制适合您的需要)。

另一个select是通过设置proxy_next_upstream error;来阻止反向代理尝试使用下一个节点,以防超时proxy_next_upstream error; 。 或者你可以设置这两个选项,如上所述。