4、读取超时与写入超时:请求处理生命周期、读取超时防止慢客户端、写入超时防止上游服务阻塞

在 API Gateway 的请求处理生命周期中,读取超时(Read Timeout)与写入超时(Write Timeout)是两个极易被混淆但职责完全不同的配置项。它们分别作用于连接建立后的数据接收阶段和响应发送阶段,直接决定了网关在面对慢客户端或慢上游服务时的行为。

4.1 请求处理生命周期中的超时位置

一个完整的 API 请求从进入网关到返回客户端,经历以下关键阶段。超时配置在每个阶段都有不同的保护目标:

阶段 超时类型 保护对象 典型配置
客户端 → 网关(请求头/体传输) 读取超时 防止慢客户端占用连接 5s – 30s
网关内部处理(路由/认证/限流) 处理超时 防止内部逻辑死循环 1s – 5s
网关 → 上游服务(请求转发) 上游连接/读取超时 防止上游无响应 3s – 10s
上游服务 → 网关(响应传输) 上游写入超时 防止上游响应体过大或过慢 5s – 30s
网关 → 客户端(响应传输) 写入超时 防止慢客户端拖垮网关 5s – 30s
关键认知:读取超时控制的是“数据进入网关”的速度,写入超时控制的是“数据离开网关”的速度。两者方向相反,但共同决定了网关连接的生命周期上限。
API Gateway 请求处理生命周期与超时位置 请求处理生命周期与超时位置 客户端 API Gateway 上游服务 读取超时 (请求体传输) 上游连接超时 (转发请求) 上游读取超时 (响应体传输) 写入超时 (响应体传输) 说明: • 读取超时:客户端发送请求体到网关的时限,防止慢客户端长期占用连接池。 • 写入超时:网关将响应体发送回客户端的时限,防止客户端接收过慢导致网关线程阻塞。 • 上游超时:网关与上游服务之间的数据传输时限,独立于客户端侧超时。

4.2 读取超时:防止慢客户端

读取超时定义的是网关在已建立连接后,等待客户端发送完整请求头或请求体的最大时间。如果客户端发送数据的速度过慢(例如弱网环境、恶意慢速攻击),网关会在超时后主动断开连接并返回 408 Request Timeout。

4.2.1 慢客户端的典型场景

  • 弱网移动端:用户在电梯、隧道等信号差区域上传大文件,TCP 窗口极小,数据包间隔超过数秒。
  • 慢速 HTTP 攻击(Slowloris):攻击者以极慢速度发送请求头,每次只发几个字节,耗尽网关连接池。
  • 大文件上传中断:客户端上传过程中暂停或崩溃,网关持续等待剩余数据。

4.2.2 配置策略

场景 推荐读取超时 说明
普通 API(小请求体) 5s – 10s 大多数正常客户端可在 1-3s 内完成请求头发送
文件上传 API(大请求体) 30s – 120s 需结合上传速率估算,避免正常大文件被误杀
WebSocket 升级 10s 升级握手完成后,读取超时应切换为 WebSocket 空闲超时
防慢速攻击 ≤ 5s 配合最小速率检查(如 1 字节/秒)更有效
避坑提示:读取超时设置过短(< 3s)会导致弱网用户频繁 408;设置过长(> 120s)则无法有效防御慢速攻击。建议结合网关的 proxy_request_buffering(请求体缓冲)功能,将完整请求体缓冲到磁盘后再转发,从而将读取超时与上游超时解耦。

4.3 写入超时:防止上游服务阻塞

写入超时定义的是网关在收到上游完整响应后,将响应数据发送回客户端的最大时间。如果客户端接收速度过慢(例如下载大文件时暂停),网关会在超时后中断写入并释放连接资源。

4.3.1 上游服务阻塞的典型场景

  • 客户端暂停下载:用户点击暂停后网关仍在缓冲区中等待 TCP 窗口释放,导致连接长期占用。
  • 响应体过大:网关向客户端传输 100MB JSON 数据时,客户端带宽不足,写入持续数分钟。
  • 客户端断开未检测:客户端异常断开但 TCP 连接未及时关闭,网关持续尝试写入。

4.3.2 配置策略

场景 推荐写入超时 说明
普通 API(小响应体) 5s – 15s 正常客户端可在 1-3s 内接收完响应
流式响应(SSE/Chunked) 30s – 60s 流式场景下写入超时应大于数据块间隔
大文件下载 120s – 300s 建议启用分块传输和断点续传,降低单连接压力
高并发网关 ≤ 10s 快速释放连接池,避免慢客户端拖垮整体吞吐
最佳实践:写入超时不应大于上游读取超时。如果上游服务在 10s 内返回响应,但网关写入超时设为 60s,则一个慢客户端可以占用连接长达 60s,导致连接池耗尽。建议写入超时 ≤ 上游读取超时 × 1.5。
读取超时与写入超时对比流程图 读取超时 vs 写入超时:触发条件与后果 读取超时(Read Timeout) 触发条件:客户端发送请求体速度 < 阈值 后果:返回 408,释放连接 写入超时(Write Timeout) 触发条件:客户端接收响应速度 < 阈值 后果:中断写入,释放连接 共同点:两者都是连接级别的超时,超时后网关主动断开 TCP 连接,释放 worker 线程和内存缓冲区。 区别:读取超时保护网关不被慢客户端拖慢请求接收;写入超时保护网关不被慢客户端拖慢响应发送。

4.4 配置示例:Nginx / Kong / Envoy

以下是在主流网关中配置读取超时与写入超时的典型方式:

4.4.1 Nginx(作为反向代理网关)

# 读取超时:等待客户端发送请求体的最大时间
proxy_read_timeout 10s;

# 写入超时:将响应发送给客户端的最大时间
proxy_send_timeout 10s;

# 上游读取超时:等待上游服务返回响应的最大时间
proxy_read_timeout 15s;

# 上游连接超时:与上游服务建立连接的最大时间
proxy_connect_timeout 5s;

4.4.2 Kong Gateway

# 通过 Kong 配置文件或 Admin API 设置
# 读取超时(upstream_read_timeout):等待上游响应的超时
# 写入超时(upstream_send_timeout):向上游发送请求的超时

# 示例:为特定 Service 设置超时
curl -X PATCH http://localhost:8001/services/example-service \
  --data "read_timeout=10000" \
  --data "write_timeout=10000" \
  --data "connect_timeout=5000"

4.4.3 Envoy Proxy

# Envoy 通过 route 或 cluster 配置超时
# 读取超时:idle_timeout + request_timeout
# 写入超时:stream_idle_timeout

clusters:
  - name: upstream_cluster
    connect_timeout: 5s
    per_connection_buffer_limit_bytes: 32768
    http_protocol_options:
      idle_timeout: 30s  # 连接空闲超时
    common_http_protocol_options:
      idle_timeout: 30s
      max_requests_per_connection: 100
实战建议:在微服务架构中,建议将读取超时和写入超时作为网关的全局默认值,然后针对特定路由(如文件上传、流式接口)进行覆盖。同时开启网关的连接池监控,当连接池使用率超过 80% 时,优先排查是否存在慢客户端导致的超时未触发。

4.5 本章小结

  • 读取超时作用于请求接收阶段,防止慢客户端或慢速攻击耗尽连接池。
  • 写入超时作用于响应发送阶段,防止慢客户端阻塞网关的响应通道。
  • 两者在生命周期中位置不同,但共同决定了网关连接的最大存活时间。
  • 配置时应结合业务场景(普通 API / 大文件 / 流式)差异化设置,避免一刀切。
  • 建议配合连接池监控和请求体缓冲功能,实现更精细的超时控制。