Nginx重试引发Http请求重复执行
1.问题背景
web站点通过Nginx做反向代理,两个tomcat做负载均衡。
web站点有一个群发短信模块,管理员可以填写发送内容,手机号列表。点击发送,后台会执行群发短信操作,在发送结束后,返回发送结果。
在很早之前遇到过,重复发送的问题。最开始以为管理员异常操作,重复点击了发送按钮。因此对前端提交做了限制。在点击发送时,把按钮置为disable,执行完毕拿到返回数据,再置为可用,从前端限制重复提交。
今天突然有同事又使用到了这个模块,分别群发了两批短信(8000条和1600条),前端都提示了操作失败,但自己的手机却分别收到了重复的短信推送。
2.问题分析
首先确认了手机号列表没有重复,也通过浏览器调试确认前端没有发起重复请求。
通过查看两台服务器的日志,确实出现了重复的发送,且发送时间点不在同一时刻。
通过日志,可以估算出,发送一条短信的时间在10毫秒。
也通过日志,查看到了处理超时日志,且发现了规律,重复的请求在不同机器处理且处理间隔时间相差一致
A日志
10:03:05,878 INFO TIMEOUT.info:252 - time: 75701ms, concurrent: 1, url: /push/sendmessage.10:07:24,705 INFO TIMEOUT.info:252 - time: 15148ms, concurrent: 0, url: /push/sendmessage.
B日志
10:03:21,599 INFO TIMEOUT.info:252 - time: 76471ms, concurrent: 1, url: /push/sendmessage.10:07:39,718 INFO TIMEOUT.info:252 - time: 15113ms, concurrent: 0, url: /push/sendmessage.
结合以上信息猜测,应该是请求执行时间超过限制,造成前端提示失败。重复的提交应该是重试机制造成。
于是联系运维进行确认。得到相关答复:
Nginx可以配置超时时间,假设配置15s,而一个请求需要16s才能处理完返回。Nginx路由到A服务器处理,A执行到第15s时,没有正常返回,Nginx会重新发到B服务处理,B执行到第15s时,也没有正常返回。前端等待30s,最后返回失败。A和B分别收到对应的请求,内部都进行了处理。
和运维同学确认过原因后,就可以根据相关信息自己搜索。
nginx的重试机制
文中也点到了一种解决方案,可以通过ip访问服务,绕过nginx。
另外我们的web站点,有一个下载的功能,在优化之前,每次下载的执行时间也很长,但是确没有遇到超时问题。分析了可能是GET和POST的区别。上文也给出了下面这个链接,确定大致思路。
http://serverfault.com/questions/528653/how-can-i-stop-nginx-from-retrying-put-or-post-requests-on-upstream-server-timeo
3.问题总结
通过网上的信息参考,Nginx可以对文件上传,下载,GET和POST请求设置不同的超时策略。
另外对于短信群发业务,其实有单独的模块,通过规则管理配置,做离线任务执行。现有保留的群发模块,只是为了应对小规模的业务。