首页
学习
活动
专区
圈层
工具
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Nginx 重试机制的踩坑

在我们日常的开发与运维工作中,总是会遇到各种意想不到的问题。最近公司的一个线上系统就出现了一个比较棘手的问题,让我们一起来看看是如何解决的。

一、问题的产生

最近几天,公司的一个线上系统出现了故障。操作人员在导入一份 Excel 后,由于处理时间比较长,前端界面一直显示在 loading 状态。最后,前端报出 “接口不可用” 的错误。后台开发人员查询数据库时,发现生成了多条导入处理任务。这时,后台、前端和运维人员开始互相指责,各说不是自己的问题,最后问题被推给了 “中间件”。

二、分析问题

1. 系统现状

想要分析问题,首先要了解系统的部署及网络情况。该系统的部署图如下:

用户在网页上操作,数据通过网络传递回服务端,这个过程使用了 CDN,然后到负载器,再分发到 Nginx,Nginx 分发到后端的服务上,最后到数据库。

这是一个前后端分离的项目,前端采用 Vue 开发,后台采用 Java 开发,数据库用的是 MySQL。

2. 排查问题

后台写入数据重复,后端开发人员第一反应是前端重复提交的问题。于是找到前端同事询问,前端表示操作上加了 loading,用户不可能点击第二次。后端开发人员不信,查看 F12 后发现没有重复的网络请求。

于是后端开发人员找到运维人员,运维人员表示服务器没有动过。大家讨论了半天,有的说是 CDN 的问题,有的说是 Nginx 的问题,众说纷纭。

3. 问题超乎想象

大家都没有头绪,于是查看 Nginx 的 access log。通过 grep 命令找到接口对应的请求记录,发现一个接口处理要 60s?

找到对应的开发人员询问情况,开发人员表示后台处理确实要超 60s。再问前端超时是多久,前端回答是 30s。这里只能说 “我服了”。正常情况下,前端操作的超时要大于后端的超时,不然后台处理完了,无法返回给前端,前端超时了,用户可能另起操作或者关闭了。

仔细查看 access log,发现同个接口请求之间间隔的时间差不多,不像是前端用户手动操作导致的。于是想到是不是 Nginx 的重试机制导致的呢?

查看该系统对应的 location 的 conf 文件,发现这个 API 对应的 location 中并未作个性化配置,都是默认配置。

三、解决问题

1.Nginx 的超时、重试机制

找到 Nginx 中用于配置 location 的超时的配置项,一共有三个:

proxy_connect_timeouttime:与后端 / 上游服务器建立连接的超时时间,默认为 60s。

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

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

失败重试机制设置:

proxy_next_upstreamerror | timeout | invalid_header | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | non_idempotent | off ... :配置什么情况下需要向上游服务器进行重试。默认为:error timeout。error 表示读写出错;timeout 表示超时;invalid_header 表示头信息有误;non_idempotent 表示 RFC - 2616 定义的非幂等 HTTP 方法(POST、LOCK、PATCH),也可以在失败后重试(默认幂等方法 GET、HEAD、PUT、DELETE、OPTIONS、TRACE);off 表示禁用重试。

proxy_next_upstream_triesnumber:设置重试次数,默认 0 表示不限制。这里的次数包含第一次请求。

proxy_next_upstream_timeouttime:设置重试最大超时时间,默认 0 表示不限制。

上面 2 个值的关系是且的关系,在限制时间内或者重试次数达到一个值就会结束重试,并返回客户端响应。

2. 结合现状分析

从官方文档中发现:指令proxy_connect_timeoutproxy_read_timeout为超时状态时,都会触发proxy_next_upstream的 timeout 条件,当条件成立时,就会执行proxy_next_upstream_tries中配置的重试次数。

当前 Nginx 的 location 中都是默认值,而后端服务的处理时长却超过 60s,已经达到触发proxy_next_upstream的条件,请求自动进入重试,从而导致后端服务处理了多次请求,写入了重复的记录。

3. 解决当前问题

确定问题后,调整相关的参数,在 location 中加入上面的几个参数,并调整下时长,如下示例:

location /xxx {

proxy_connect_timeout 5s;

proxy_read_timeout 100s;

proxy_send_timeout 70s;

proxy_next_upstream error timeout;

proxy_next_upstream_timeout 70;

proxy_next_upstream_tries 1;

proxy_pass http://backend_server;

add_header upstream_addr $upstream_addr;

}

重新 reload 下 Nginx 的配置,让用户重新操作一次。虽然界面还是在 loading,但观察数据库中的记录,已经没有重复写入了,并且前端响应也变快了。

四、总结

虽然解决了问题,但处理时间超过 60s,这很不正常。要求开发人员排查到底是哪里慢。如果确实需要这么久,要做成异步处理,不能一直让前端 loading,这样用户体验不好。

另外,这个问题是通过中间件解决的重复问题,其实本质上是接口幂等性的问题,应该采用幂等性的处理规则来解决。比如在前端的请求加上一个唯一标识,后台通过对标识的加锁,或者数据库唯一主键、或者文件的 MD5 码等来解决。

以上就是这次 Nginx 重试机制问题的整个解决过程,希望能给大家带来一些启发和帮助。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OTkeT4TinCo7jojcufshO87w0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券