bigywater.tech
微服务 · 21

超时三件套:连接、发送、读取超时分别在什么时候触发

nginx 的连接、发送、读取三个超时到底各管什么?一个一点一点滴出来的响应会不会超时?超时遇上重试,client 要等多久?这篇用原始 socket 扮演各种坏上游把它们量出来。

3.25 s
上游每 0.4 s 发 1 个字节、读取超时 1 s 时,整个响应的耗时;读取超时一次也没有触发
2.01 s
两个上游都沉默时,GET 在默认配置下的等待时间(两个读取超时之和);POST 只等 1.00 s
1.00 s
proxy_next_upstream_timeout 800ms 时 GET 的等待时间;设成 1500ms 时还是 2.01 s

三个超时

实测 · 本机执行输出
  超时     | client 看到 | client 等了                             | error.log 里的原因                                                                                                                                        
  -------+-----------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------
  (1) 连接 | 504       | 1.01 s                                | 83299#0: *2 upstream timed out (60: Operation timed out) while connecting to upstream, client: 127.0.0.1, server: , request: "GET / HTTP/1.1", upstrea
  (2) 发送 | 504       | 1.04 s(含 client 把 30 MB 交给 nginx 的时间) | 83305#0: *2 upstream timed out (60: Operation timed out) while sending request to upstream, client: 127.0.0.1, server: , request: "POST / HTTP/1.1", u
  (3) 读取 | 504       | 1.00 s                                | 83314#0: *2 upstream timed out (60: Operation timed out) while reading response header from upstream, client: 127.0.0.1, server: , request: "GET / HTT

连接、发送、读取分别在不同阶段触发,都回 504,区别在 error.log 里。

读取超时的含义

实测 · 本机执行输出
  上游行为                   | client 看到的状态码  | client 收到的响应体 | client 等了 | error.log                                                                                                                                   
  -----------------------+----------------+---------------+-----------+---------------------------------------------------------------------------------------------------------------------------------------------
  (a) 先沉默 1.5 s 再回       | 504            | 167 字节(声明 8)  | 1.01 s    | 83374#0: *2 upstream timed out (60: Operation timed out) while reading response header from upstream, client: 127.0.0.1, server: , request: 
  (b) 头先到,之后每 0.4 s 一个字节 | 200            | 8 字节(声明 8)    | 3.25 s    |                                                                                                                                             
  (c) 头和 4 字节先到,停 1.5 s  | IncompleteRead | 0 字节(声明 8)    | 1.00 s    | 83547#0: *2 upstream timed out (60: Operation timed out) while reading upstream, client: 127.0.0.1, server: , request: "GET / HTTP/1.1", ups
  (b) 整个响应花了 3.2 s,但每次读之间最多隔 0.4 s,从来没触发 1 s 的读取超时;(c) 响应头已经发给 client(状态码 200)之后上游才卡住,nginx 无法再改状态码,只能把连接断掉,client 收到的是残缺的响应体

是两次读之间的间隔,不是总时长;响应头发出后上游卡住,nginx 只能断开连接。

超时与重试

实测 · 本机执行输出
  配置                           | GET:状态码 · 等了 | POST:状态码 · 等了
  -----------------------------+--------------+--------------
  默认                           | 504 · 2.01 s | 504 · 1.00 s 
  next_upstream_tries 1        | 504 · 1.00 s | 504 · 1.00 s 
  next_upstream_timeout 1500ms | 504 · 2.01 s | 504 · 1.01 s 
  next_upstream_timeout 800ms  | 504 · 1.00 s | 504 · 1.00 s 
  默认配置下 GET 先在第一个上游等满 1 s 超时,再去第二个上游又等满 1 s,client 一共等了约两倍的读取超时;POST 请求已经发给上游,出错后默认不再重试(非幂等),所以只等一个读取超时;next_upstream_tries 1 让 GET 也只试一次。proxy_next_upstream_timeout 限制的是“还允许换上游重试的时间窗”,不是总时长:设 1500ms 时第二次尝试在 1.0 s 开始(窗口还没过),它仍然跑满自己的 1 s 读取超时,总共还是约 2 s;设 800ms 时 1.0 s 已经超出窗口,不再换上游,总共约 1 s

GET 会在下一个上游再等满一个超时;next_upstream_timeout 限制的是还能重试的窗口。

分步演示

超时三件套
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Go / nginx / Envoy / etcd / Consul / Nacos / OpenTelemetry / Prometheus(按各篇注明用到哪些及版本);所有服务都是本机自己起的进程,端口 9150 以上,只监听 127.0.0.1,不连任何外部服务,不碰本机已有的实例;故障用 kill -9 进程、让连接断开或在代码里注入延迟与错误来制造;计时用 time.perf_counter,数字每次运行会有小幅抖动。nginx 是 Homebrew 的 1.31.6;“上游”是我写的原始 TCP 服务(各种坏行为);连接超时用的是 192.0.2.1:80(保留的文档地址,SYN 发出后没有任何回应,数据包会离开本机);发送超时用的是 SO_RCVBUF 4096 且从不读取的上游,POST 30 MB;第三节两个 server 都写了 max_fails=0。每个场景只测了一次
    • 没有验证:proxy_send_timeout 在 proxy_request_buffering off(流式转发请求体)时的行为;HTTP/2 上游、keepalive 上游下超时的差别;client_header_timeout、client_body_timeout、send_timeout 等面向 client 一侧的超时;上游返回 502 与 504 的更多触发条件
    京ICP备2026067502号-1京公网安备11010502063487号