后端挂一台实例时,Nginx 会把请求转给 upstream 里的下一台。这个行为很有用,但配置不当也会带来两个问题:一是写请求被重复执行,二是故障时重试把压力成倍放大到剩余实例上。本文梳理 proxy_next_upstream 相关指令的默认行为和一组更稳妥的配置。
proxy_next_upstream 默认值为 error timeout:连接出错、发送或读取响应超时时,会换下一台。non_idempotent 才会重试。proxy_connect_timeout、proxy_send_timeout、proxy_read_timeout 默认都是 60 秒,具体以所用版本的官方文档为准。max_fails 默认 1,fail_timeout 默认 10 秒:10 秒内失败达到次数,该节点会被暂时摘除 10 秒。有人为了“提高成功率”写成:
proxy_next_upstream error timeout http_502 http_503 non_idempotent;读超时时后端可能已经处理完请求,只是响应没回来。重试到下一台会让下单、扣款这类接口执行两次。除非接口自身有幂等键,否则不要加 non_idempotent。
默认情况下,Nginx 会遍历 upstream 中的全部节点。节点多、超时又长时,一个请求可能占用连接很久,故障时后端承受的请求量近似乘以重试次数。应显式限制:
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 5s;tries 限制总尝试次数(含第一次),timeout 限制整个重试过程的总时长,两者都为 0 表示不限制。
500 往往是业务逻辑异常,换一台大概率还是 500,只会增加负载。一般只对 http_502、http_503、http_504 这类“节点不可用”信号重试,并且只针对幂等的读请求。
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 延迟调整。
读写差异大时,可以按路径拆开:
location /api/order/ {
proxy_pass http://app;
proxy_next_upstream error;
proxy_next_upstream_tries 1;
}error 只覆盖连接建立阶段的失败;若请求尚未发出就失败,换节点是安全的。把 tries 设为 1 则完全不重试,由客户端凭幂等键决定是否重发。
curl -s -o /dev/null -w '%{http_code} %{time_total}\n' 连续请求,观察耗时是否被 proxy_next_upstream_timeout 约束。$upstream_addr 和 $upstream_status,一次重试会在字段里以逗号分隔显示多个节点,可据此统计重试比例。proxy_read_timeout),确认数据库里只产生一条记录。non_idempotent,幂等要靠业务层保证。proxy_next_upstream_tries 与 proxy_next_upstream_timeout,防止故障放大。$upstream_addr,让重试行为可观测。原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。