Envoy timeout and nginx timeout
Envoy 超时机制
Envoy 中有多个名字相近的超时参数,但它们管辖的连接段、生命周期粒度和触发行为各不相同。本文从连接拓扑出发,系统梳理 Cluster idleTimeout、connectTimeout、streamIdleTimeout、TcpProxy idleTimeout 以及 Route timeout 的核心语义与配置要点。
连接拓扑总览
理解所有超时参数的前提,是先建立清晰的连接拓扑模型:
1 | Downstream Envoy Upstream |
三个核心参数分别管辖不同的段:
| 参数 | 管辖段 | 生命周期粒度 |
|---|---|---|
HCM.commonHttpProtocolOptions.idleTimeout |
[A] 下游连接 |
连接级(TCP 连接) |
Cluster.commonHttpProtocolOptions.idleTimeout |
[B] 上游连接 |
连接级(TCP 连接) |
HCM.streamIdleTimeout |
[A] 中的 Stream |
请求级(单个 HTTP 请求) |
Cluster idleTimeout — 上游连接空闲超时
cluster.commonHttpProtocolOptions.idleTimeout 控制的是 Envoy → Upstream 这段上游连接的空闲超时。
定义
1 | clusters: |
空闲的定义:该连接上没有任何活跃的 HTTP 请求/Stream。
行为流程
1 | Envoy 与 Upstream 建立 HTTP 连接 |
Cluster connectTimeout — 上游 TCP 握手超时
connectTimeout 控制的是 Envoy 与 Upstream 建立 TCP 连接的超时时间,是连接生命周期中最早发生的超时。
行为流程
1 | Envoy 发起连接 |
关键注意点
connectTimeout只管 TCP 握手阶段,握手完成后即交棒给其他超时参数。- 不要设置过大:
connectTimeout过大会导致故障时请求长时间 pending,拖垮连接池。
streamIdleTimeout — 请求 Stream 空闲超时
streamIdleTimeout 控制的是单个 HTTP 请求的 Stream 空闲超时。即一个请求在处理过程中,如果连续一段时间没有任何数据收发,则强制终止该 Stream。
行为流程
1 | 收到请求 Headers |
关键词是”空闲”:只要有数据在流动(哪怕是 chunked body 的一个 chunk),计时器就会重置。
两个配置层级
streamIdleTimeout 有两个配置层级,语义略有不同:
1. HttpConnectionManager 级别(下游连接)
控制 Downstream → Envoy 这段 Stream 的空闲超时:
1 | - name: envoy.filters.network.http_connection_manager |
2. Route 级别(会覆盖 HCM 的值)
1 | routes: |
Route 级别的
idle_timeout会覆盖 HCM 级别的stream_idle_timeout。
TcpProxy idleTimeout — L4 层隧道空闲超时
TcpProxy 的 idleTimeout 工作在 L4 层,与前面讨论的 HTTP 层超时有本质区别。
管辖范围
1 | Downstream Envoy Upstream |
行为流程
1 | Client ──── TCP 连接 ────► Envoy ──── TCP 连接 ────► Upstream |
L4 vs L7 的”空闲”定义对比
| 层级 | “空闲”的定义 | 计时器重置条件 |
|---|---|---|
| L7 idleTimeout | 没有活跃的 HTTP 请求/Stream | 新请求到来 |
| L4 idleTimeout | 没有任何 TCP 字节传输 | 任意字节传输(含心跳包、keep-alive 探针) |
L4 层只要有任何字节流动,计时器就会重置,粒度远比 L7 粗。
Route timeout — 请求总时长上限
Route 的 timeout 是请求的总时长上限,从 Envoy 转发请求给 Upstream 开始计时,到收到完整响应为止,中间不会重置。
行为流程
1 | Client Envoy Upstream |
Envoy 超时参数 vs Nginx 超时参数:区别与联系
Envoy 和 Nginx 都是主流的反向代理/负载均衡器,但它们的超时体系设计哲学有所不同。Nginx 以”指令式配置”为主,参数语义直观;Envoy 则以”连接拓扑分层”为核心,粒度更细、更适合云原生场景。下面从对应关系和核心差异两个维度展开对比。
参数对应关系总览
先建立一个直观的映射表,方便快速定位:
| Envoy 参数 | 对应 Nginx 参数 | 管辖段 | 超时类型 |
|---|---|---|---|
Cluster connectTimeout |
proxy_connect_timeout |
Envoy → Upstream TCP 握手 | 连接建立 |
Cluster idleTimeout |
keepalive_timeout(upstream 侧) |
Envoy → Upstream 连接空闲 | 连接保活 |
HCM idleTimeout |
keepalive_timeout(client 侧) |
Client → Envoy 连接空闲 | 连接保活 |
streamIdleTimeout |
proxy_read_timeout |
单个请求 Stream 数据流动 | 数据传输 |
Route timeout |
proxy_read_timeout + proxy_send_timeout |
请求总时长 | 端到端请求 |
TcpProxy idleTimeout |
proxy_timeout(stream 模块) |
L4 整条 TCP 隧道 | 字节级空闲 |
注意:这是语义近似的映射,并非完全等价,差异见下文详述。
连接建立阶段:connectTimeout vs proxy_connect_timeout
两者语义最为接近,都是控制与上游建立 TCP 连接的超时时间。[2]
1 | # Nginx |
差异点
- Nginx 的
proxy_connect_timeout默认值为 60s,且仅适用于 HTTP upstream 模块。 - Envoy 的
connectTimeout没有默认值,必须显式配置,否则使用全局默认(通常为 5s)。 - Envoy 在连接超时后可结合
retryPolicy自动重试;Nginx 需要额外配置proxy_next_upstream timeout才能实现类似效果。[1]
连接保活阶段:idleTimeout vs keepalive_timeout
这是两者差异最大的地方,需要重点区分。[4]
Nginx keepalive_timeout
1 | # 控制 Client → Nginx 这段下游连接的保活时间 |
Envoy idleTimeout(两个方向分开配置)
1 | # 下游连接(HCM 级别) |
核心差异
| 维度 | Nginx keepalive_timeout | Envoy idleTimeout |
|---|---|---|
| 下游/上游 | 同一个指令,语境不同 | 明确拆分为两个参数 |
| HTTP/2 支持 | 有限(需额外模块) | 原生支持,发送 GOAWAY 帧 |
| 默认值 | 65s(下游)/ 需显式配置(上游) | 1h(上下游均为 1h) |
| 连接池集成 | 需手动开启 keepalive 指令 |
连接池原生内置,自动管理 |
数据传输阶段:streamIdleTimeout vs proxy_read_timeout
这两个参数最容易被混淆,但语义有微妙差异。[3]
Nginx proxy_read_timeout
1 | # 两次连续读操作之间的最大间隔,不是请求总时长 |
关键:
proxy_read_timeout是两次读操作之间的间隔超时,每次收到数据都会重置计时器。
Envoy streamIdleTimeout
1 | http_connection_manager: |
关键:
streamIdleTimeout是 Stream 上任意数据传输(Headers/Body chunk)都会重置计时器。
差异对比
| 维度 | Nginx proxy_read_timeout | Envoy streamIdleTimeout |
|---|---|---|
| 计时重置条件 | 收到任意响应数据 | 任意方向有数据传输(含请求体) |
| 超时行为 | 返回 504 | 终止 Stream,返回 408 |
| 作用方向 | 仅 Upstream → Nginx 方向 | 双向(含 Client → Envoy 上传) |
| 默认值 | 60s | 5min |
| Route 级别覆盖 | 不支持 per-route 配置 | 支持 Route 级别 idle_timeout 覆盖 |
请求总时长:Route timeout vs proxy_read/send_timeout
Nginx 的”总时长”是组合概念
Nginx 没有单一的”请求总时长”参数,而是由多个参数共同构成:[2]
1 | proxy_connect_timeout 10s; # TCP 握手 |
Envoy Route timeout 是严格的总时长
1 | routes: |
| 维度 | Nginx | Envoy Route timeout |
|---|---|---|
| 总时长控制 | 多参数组合,非严格总时长 | 单一参数,严格端到端总时长 |
| 超时后行为 | 返回 504 | 返回 504 |
| 计时器重置 | 每次读/写操作重置 | 不重置,单调递增 |
| 禁用方式 | 设置极大值 | 设置 timeout: 0s 显式禁用 |
| Per-route 配置 | 需要 location 块单独配置 |
原生支持 per-route 配置 |
L4 层:TcpProxy idleTimeout vs proxy_timeout(stream 模块)
当 Nginx 工作在 TCP 代理模式(stream 模块)时,对应 Envoy 的 TcpProxy idleTimeout:[2]
1 | # Nginx stream 模块 |
1 | # Envoy TcpProxy |
两者语义高度一致,都是 L4 字节级别的空闲超时,任意方向有字节传输即重置计时器。
核心设计哲学差异总结
Nginx 和 Envoy 在超时设计上的根本差异,源于它们的定位不同:
| 维度 | Nginx | Envoy |
|---|---|---|
| 设计哲学 | 指令式,参数直观易懂 | 拓扑分层,精确管辖每段连接 |
| HTTP/2 原生支持 | 有限 | 完整支持(GOAWAY、RST_STREAM) |
| Per-route 超时 | 需要 location 块 |
原生 per-route 配置 |
| 连接池管理 | 需手动开启 keepalive | 内置连接池,自动管理 |
| 上下游超时分离 | 部分分离 | 完全分离,语义清晰 |
| 动态配置 | 需 reload | xDS 动态下发,无需重启 |
| 可观测性 | 基础日志 | 内置 metrics、tracing、stats |
迁移建议速查
如果你正在从 Nginx 迁移到 Envoy,以下是参数映射的快速参考:
1 | Nginx → Envoy |
💡 总结
Envoy 的超时体系按连接拓扑分层设计,每个参数都有其精确的管辖边界:
connectTimeout→ 最早,只管 TCP 握手阶段HCM idleTimeout→ 管下游 TCP 连接的空闲回收Cluster idleTimeout→ 管上游 TCP 连接的空闲回收(连接池复用)streamIdleTimeout→ 管单个请求 Stream 的数据流动,超时返回 408Route timeout→ 管请求的总端到端时长,超时返回 504TcpProxy idleTimeout→ L4 层整条隧道的字节级空闲超时