Nginx重试机制,浏览器重复请求两次多次

场景还原#

问题 用户再浏览器里执行了一次http请求,结果后端服务器执行了两遍,如果这次请求是Insert操作,可想而知,数据库会多出一条一模一样的记录来。

  • 网关用Nginx做了反向代理和负载均衡,Nginx下挂着两台阿里云ECS服务器,每台机器上都装着Tomcat,用户打开浏览器,点击页面,访问后端接口,查看Nginx的access.log,结果这一条请求打在了两台服务器上。

问题剖析#

nginx的重试机制就是容错的一种,在nginx的配置文件中,proxy_next_upstream项定义了什么情况下进行重试,官网文档中给出的说明如下:

Copy Highlighter-hljs
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,官方文档中给出的说明如下:
Copy Highlighter-hljs
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的重试机制,配置如下

Copy Highlighter-hljs
server { location ~ /api/insertData { proxy_connect_timeout 60; proxy_send_timeout 60; proxy_read_timeout 60; proxy_next_upstream off; } }

也可以直接关闭重试机制

Copy Highlighter-hljs
proxy_next_upstream off;

nginx timeout 配置 全局timeout 局部timeout web timeout#

nginx比较强大,可以针对单个域名请求做出单个连接超时的配置.

比如些动态解释和静态解释可以根据业务的需求配置

proxy_connect_timeout :后端服务器连接的超时时间_发起握手等候响应超时时间

proxy_read_timeout:连接成功后_等候后端服务器响应时间_其实已经进入后端的排队之中等候处理(也可以说是后端服务器处理请求的时间)

proxy_send_timeout :后端服务器数据回传时间_就是在规定时间之内后端服务器必须传完所有的数据

nginx使用proxy模块时,默认的读取超时时间是60s。

1、请求超时

Copy Highlighter-hljs
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、后端服务器处理请求的时间设置(页面等待服务器响应时间)

Copy Highlighter-hljs
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这类情况,可以适当调整各类缓冲区大小来满足实际需求:

Copy Highlighter-hljs
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;
posted @   caibaotimes  阅读(4641)  评论(1编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示
CONTENTS