首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Nginx 反向代理重试的边界:proxy_next_upstream 如何避免重复提交与雪崩放大

Nginx 反向代理重试的边界:proxy_next_upstream 如何避免重复提交与雪崩放大

原创
作者头像
一只特立独行的兔先生
发布于 2026-10-29 20:07:08
发布于 2026-10-29 20:07:08
100
举报

背景

后端挂一台实例时,Nginx 会把请求转给 upstream 里的下一台。这个行为很有用,但配置不当也会带来两个问题:一是写请求被重复执行,二是故障时重试把压力成倍放大到剩余实例上。本文梳理 proxy_next_upstream 相关指令的默认行为和一组更稳妥的配置。

默认行为先搞清楚

  • proxy_next_upstream 默认值为 error timeout:连接出错、发送或读取响应超时时,会换下一台。
  • 自 1.9.13 起,非幂等请求(POST、LOCK、PATCH)在请求已发给后端后,默认不会再被转发到下一台;只有显式写上 non_idempotent 才会重试。
  • proxy_connect_timeout、proxy_send_timeout、proxy_read_timeout 默认都是 60 秒,具体以所用版本的官方文档为准。
  • upstream 中 max_fails 默认 1,fail_timeout 默认 10 秒:10 秒内失败达到次数,该节点会被暂时摘除 10 秒。

常见坑

1. 为 POST 打开 non_idempotent

有人为了“提高成功率”写成:

代码语言:nginx
复制
proxy_next_upstream error timeout http_502 http_503 non_idempotent;

读超时时后端可能已经处理完请求,只是响应没回来。重试到下一台会让下单、扣款这类接口执行两次。除非接口自身有幂等键,否则不要加 non_idempotent。

2. 重试次数没有上限

默认情况下,Nginx 会遍历 upstream 中的全部节点。节点多、超时又长时,一个请求可能占用连接很久,故障时后端承受的请求量近似乘以重试次数。应显式限制:

代码语言:nginx
复制
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 5s;

tries 限制总尝试次数(含第一次),timeout 限制整个重试过程的总时长,两者都为 0 表示不限制。

3. 把 http_500 当成可重试

500 往往是业务逻辑异常,换一台大概率还是 500,只会增加负载。一般只对 http_502、http_503、http_504 这类“节点不可用”信号重试,并且只针对幂等的读请求。

推荐配置

代码语言:nginx
复制
upstream app {
    server 10.0.0.11:8080 max_fails=2 fail_timeout=10s;
    server 10.0.0.12:8080 max_fails=2 fail_timeout=10s;
}

server {
    location / {
        proxy_pass http://app;
        proxy_connect_timeout 2s;
        proxy_read_timeout 15s;
        proxy_next_upstream error timeout http_502 http_503;
        proxy_next_upstream_tries 2;
        proxy_next_upstream_timeout 5s;
    }
}

要点:连接超时设短,让坏节点尽早被发现;读超时按接口真实耗时设置;重试次数与总时长都有上限。上面的数值只是示例,需结合自己业务的 P99 延迟调整。

写接口单独处理

读写差异大时,可以按路径拆开:

代码语言:nginx
复制
location /api/order/ {
    proxy_pass http://app;
    proxy_next_upstream error;
    proxy_next_upstream_tries 1;
}

error 只覆盖连接建立阶段的失败;若请求尚未发出就失败,换节点是安全的。把 tries 设为 1 则完全不重试,由客户端凭幂等键决定是否重发。

验证方法

  1. 在测试环境停掉一台后端,用 curl -s -o /dev/null -w '%{http_code} %{time_total}\n' 连续请求,观察耗时是否被 proxy_next_upstream_timeout 约束。
  2. 在日志格式中加入 $upstream_addr 和 $upstream_status,一次重试会在字段里以逗号分隔显示多个节点,可据此统计重试比例。
  3. 对写接口做超时注入(后端 sleep 超过 proxy_read_timeout),确认数据库里只产生一条记录。

小结

  • 重试只应用于“请求没有产生副作用”的失败。
  • 写请求不要开 non_idempotent,幂等要靠业务层保证。
  • 总是同时设置 proxy_next_upstream_tries 与 proxy_next_upstream_timeout,防止故障放大。
  • 日志里记录 $upstream_addr,让重试行为可观测。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 背景
  • 默认行为先搞清楚
  • 常见坑
    • 1. 为 POST 打开 non_idempotent
    • 2. 重试次数没有上限
    • 3. 把 http_500 当成可重试
  • 推荐配置
  • 写接口单独处理
  • 验证方法
  • 小结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档