浏览器访问管理服务器超时导致请求重试问题分析

目录

背景

分析

重新发起请求原因

真正的原因

网上案例

管理项目案例分析

总结


背景

管理后台上有个批量退款的功能,每次操作退两百笔退款订单,大概会执行4分钟的时间。发现相同的参数和请求路径,每隔两分钟进行重试,总共重试了四次,不像是人为的原因。

分析

1、谷歌浏览器超过2分钟没有收到服务方的请求,就会进行断开连接。这时就猜测考虑是不是业务处理逻辑太久了,导致浏览器长时间没有接受到后台的返回结果,自动重复提交了记录。

3、之前是在管理项目上出现的问题,利用arthas注入程序执行时睡眠10分钟的代码,复现了。

重新发起请求原因

HTTP/1.1 的一处规范

If an HTTP/1.1 client sends a request which includes a request body, but which does not
 include an Expect request-header field with the "100-continue" expectation, and if the 
client is not directly connected to an HTTP/1.1 origin server, and if the client sees the 
connection close before receiving any status from the server, the client SHOULD retry the request.

大致意思就是说,如果发送一个请求到服务器端,该请求有请求体,但是请求头里面不包含“ 100-continue ”这种东西,并且客户端没有直接连接到原始的 HTTP/1.1 服务器,此时,如果客户端在接收到服务器发送的 HTTP 状态之前发现服务器主动关掉连接,那么客户端应该重试请求。

那看起来好像就是服务器端主动关掉了连接,导致浏览器重新发送请求了。

真正的原因

网上案例

如果服务利用nginx做反向代理, nginx配置有超时控制,超过60s时,就中断了请求,然后谷歌浏览器超过两分钟就重新发起了请求了。

send_timeout的默认值为60s, 配置区域:http server location。

说明:设置响应传输到客户端的超时时间。仅在两个连续的写操作之间设置超时,而不是为整个

响应的传输。如果客户端在此时间内未收到任何内容,则会关闭连接。

可以查看nginx配置的超时时间,判断是否有如下配置:

server {

send_timeout 60s; // 服务端向客户端传输数据的超时时间

}

这样看就完全满足上述重发请求的条件了:

  • 请求头里面不包含“ 100-continue ”
  • 客户端没有连接到原始HTTP服务器(Nginx做了反向代理)
  • Nginx超过60s主动关掉了连接

管理项目案例分析

前端Ajax超时配置分析

浏览器通过Ajax访问时的网络连接/读/写超时时间。

默认情况下未配置,即未启用超时。指定超时值以毫秒为单位设置请求超时,这将覆盖全局设置。

它应该基于每个浏览器是否有XMLHttpRequest对象的超时处理。Timeout unsigned long请求在自动终止之前可以花费的毫秒数。值为0(这是默认值)表示没有超时。

官网说明

timeout

Type: Number

Set a timeout (in milliseconds) for the request. This will override any global timeout set
 with $.ajaxSetup(). The timeout period starts at the point the$.ajaxcall is made; if 
several other requests are in progress and the browser has no connections available, it is 
possible for a request to time out before it can be sent. In jQuery 1.4.x and below, the 
XMLHttpRequest object will be in an invalid state if the request times out; accessing any 
object members may throw an exception. In Firefox 3.0+ only, script and JSONP requests 
cannot be cancelled by a timeout; the script will run even if it arrives after the timeout period.

浏览器的默认超时时间配置分析

Chrome 浏览器的默认值是5分钟,如下图:

 相关浏览器代码地址如下:https://source.chromium.org/chromium/chromium/src/+/main:net/socket/client_socket_pool.cc;l=25

管理服务的nginx超时配置分析

nginx没有配置proxy_connect_timeout、proxy_read_timeout、proxy_send_timeout相关的参数

网络连接/读/写超时设置相关说明如下:

proxy_connect_timeout time:与后端/上游服务器建立连接的超时时间,默认为60s,此时间不超过75s。

proxy_read_timeout time:设置从后端/上游服务器读取响应的超时时间,默认为60s,此超时时间指的是两次成功读操作间隔时间,而不是读取整个响应体的超时时间。如果在此超时时间内上,游服务器没有发送任何响应,则Nginx关闭此连接。

proxy_send_timeout time:设置往后端/上游服务器发送请求的超时时间,默认为60s,此超时时间指的是两次成功写操作间隔时间,而不是发送整个请求的超时时间。如果在此超时时间内上,游服务器没有接收任何响应,则Nginx关闭此连接。

进行测试

让sre帮忙将管理平台的nginx配置改成600秒,如下:

proxy_connect_timeout 600;

proxy_read_timeout 600;

proxy_send_timeout 600;

测试结果如下:

没改nginx配置前

配置nginx配置

服务侧代码改成为10分钟后返回结果

每两分钟重试一次

浏览器两分钟后显示请求结果为504状态

日志显示

1、每十分钟返回处理结果

2、每六分钟重试一次,重试多次

浏览器显示无法访问此网站

服务侧代码改成为5分钟后返回结果

每两分钟重试一次

浏览器两分钟后显示请求结果为504状态

日志显示

1、每五分钟返回处理结果

2、每六分钟重试一次,只重试两次

浏览器显示无法访问此网站

服务侧代码改成为4分钟后返回结果

每两分钟重试一次

浏览器两分钟后显示请求结果为504状态

日志显示

1、四分钟返回处理结果

2、服务端不再有重试的日志

浏览器显示成功接收到请求结果

总结

浏览器发起请求,当服务端的响应时间需要很久时,需要考虑好请求链接各环节的超时配置。

本次请求问题,谷歌浏览器默认超时配置是5分钟,管理平台的nginx超时配置有问题,导致nginx提前主动关闭了链接,进而浏览器主动发起了重试请求。

本次修改方案:因为退款200笔订单耗时在4分钟左右,所以本次只改了管理项目的nginx配置,如下:

server {

...

proxy_connect_timeout 600;

proxy_read_timeout 600;

proxy_send_timeout 600;

}

浏览器超时5分钟,为什么重试是2分钟自动重试?

猜测:nginx中包含多种不同的IO处理函数。假设nginx事件处理使用的epoll模型,为事件设置了一个超时定时器,从而能够处理事件超时的情况,一般都会循环调用epoll_wait监听所有fd,处理发生的读写事件。在第一次读取时没有超时,然后执行第二次读取时超时了,就标记该事件过期了,然后nginx就主动调用了断开连接。这样两次执行的耗时也刚好是两分钟。

注意:依旧要小心响应时间超过5分钟的请求,因为谷歌浏览器默认超时配置是5分钟,服务端的响应时间超过5分钟时,浏览器会发起重试。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值