envoy timeout

Envoy timeout and nginx timeout

Envoy 超时机制

Envoy 中有多个名字相近的超时参数,但它们管辖的连接段、生命周期粒度和触发行为各不相同。本文从连接拓扑出发,系统梳理 Cluster idleTimeoutconnectTimeoutstreamIdleTimeoutTcpProxy idleTimeout 以及 Route timeout 的核心语义与配置要点。

连接拓扑总览

理解所有超时参数的前提,是先建立清晰的连接拓扑模型:

1
2
3
4
Downstream                    Envoy                         Upstream
Client ──────[A]────── HCM / Listener ──────[B]────── Cluster
↑ ↑ ↑
下游 TCP 连接 Stream(请求级) 上游 TCP 连接

三个核心参数分别管辖不同的段:

参数 管辖段 生命周期粒度
HCM.commonHttpProtocolOptions.idleTimeout [A] 下游连接 连接级(TCP 连接)
Cluster.commonHttpProtocolOptions.idleTimeout [B] 上游连接 连接级(TCP 连接)
HCM.streamIdleTimeout [A] 中的 Stream 请求级(单个 HTTP 请求)

Cluster idleTimeout — 上游连接空闲超时

cluster.commonHttpProtocolOptions.idleTimeout 控制的是 Envoy → Upstream 这段上游连接的空闲超时。

定义

1
2
3
4
5
6
clusters:
- name: my_service
common_http_protocol_options:
idle_timeout: 90s # 连接空闲超过 90s 则主动关闭

# 默认值为 1 小时

空闲的定义:该连接上没有任何活跃的 HTTP 请求/Stream。

行为流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Envoy 与 Upstream 建立 HTTP 连接


请求完成,连接进入空闲状态


空闲计时器启动(idle_timeout = 90s)

┌────┴────┐
│新请求到来│ → 计时器重置,连接复用
└─────────┘

计时器到期


Envoy 主动关闭连接
HTTP/1.1 → 发送 Connection: close
HTTP/2 → 发送 GOAWAY 帧

Cluster connectTimeout — 上游 TCP 握手超时

connectTimeout 控制的是 Envoy 与 Upstream 建立 TCP 连接的超时时间,是连接生命周期中最早发生的超时。

行为流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Envoy 发起连接


TCP SYN 发出
│ ← connectTimeout 计时开始

等待 TCP SYN-ACK

┌────┴────┐
│超时到期 │ → 连接失败,触发重试或返回 503
└─────────┘

TCP 握手完成 ← connectTimeout 计时结束

(TLS 握手,若有)

发送 HTTP 请求 ← 进入 idleTimeout 管辖

关键注意点

  • connectTimeout 只管 TCP 握手阶段,握手完成后即交棒给其他超时参数。
  • 不要设置过大:connectTimeout 过大会导致故障时请求长时间 pending,拖垮连接池。

streamIdleTimeout — 请求 Stream 空闲超时

streamIdleTimeout 控制的是单个 HTTP 请求的 Stream 空闲超时。即一个请求在处理过程中,如果连续一段时间没有任何数据收发,则强制终止该 Stream。

行为流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
收到请求 Headers


转发给 Upstream ← streamIdleTimeout 计时开始


等待 Upstream 响应

每次有数据传输 ← 计时器重置
(Headers / Body chunk)

┌────┴──────┐
│空闲超时到期│ → 重置 Stream,返回 408
└───────────┘

响应完整接收 ← streamIdleTimeout 计时结束

关键词是”空闲”:只要有数据在流动(哪怕是 chunked body 的一个 chunk),计时器就会重置。

两个配置层级

streamIdleTimeout 有两个配置层级,语义略有不同:

1. HttpConnectionManager 级别(下游连接)

控制 Downstream → Envoy 这段 Stream 的空闲超时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- name: envoy.filters.network.http_connection_manager
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
httpFilters:
- name: envoy.filters.http.router
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
internalAddressConfig: {}
routeConfig:
name: http_redirect_81_routes
virtualHosts:
- domains:
- '*'
name: redirect
routes:
- match:
prefix: /
redirect:
httpsRedirect: true
statPrefix: http_redirect_81
streamIdleTimeout: 3600s

2. Route 级别(会覆盖 HCM 的值)

1
2
3
4
5
6
7
routes:
- match:
prefix: "/upload"
route:
cluster: upload_service
timeout: 0s # 关闭整体超时
idle_timeout: 300s # 单独设置该路由的 stream idle timeout

Route 级别的 idle_timeout覆盖 HCM 级别的 stream_idle_timeout

TcpProxy idleTimeout — L4 层隧道空闲超时

TcpProxy 的 idleTimeout 工作在 L4 层,与前面讨论的 HTTP 层超时有本质区别。

管辖范围

1
2
3
4
5
Downstream                    Envoy                         Upstream
Client ──────[A]────── TcpProxy Filter ──────[B]────── Cluster

整条 TCP 隧道(A + B 作为整体)
idleTimeout 管辖的是这整段

行为流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Client ──── TCP 连接 ────► Envoy ──── TCP 连接 ────► Upstream

任意方向有字节传输 ← 计时器重置
(Client→Upstream
或 Upstream→Client)

连续无字节传输
超过 idle_timeout


Envoy 同时关闭两端连接
┌─────────────────────┐
│ 下游连接 FIN/RST │
│ 上游连接 FIN/RST │
└─────────────────────┘

L4 vs L7 的”空闲”定义对比

层级 “空闲”的定义 计时器重置条件
L7 idleTimeout 没有活跃的 HTTP 请求/Stream 新请求到来
L4 idleTimeout 没有任何 TCP 字节传输 任意字节传输(含心跳包、keep-alive 探针)

L4 层只要有任何字节流动,计时器就会重置,粒度远比 L7 粗。

Route timeout — 请求总时长上限

Route 的 timeout请求的总时长上限,从 Envoy 转发请求给 Upstream 开始计时,到收到完整响应为止,中间不会重置

行为流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Client          Envoy                    Upstream
│ │ │
│──── 请求 ────►│ │
│ │──── 转发请求 ──────────►│ ← timeout 计时开始
│ │ │
│ │ 等待响应 │
│ │ │
│ │◄─── 响应 Headers ───────│
│ │◄─── 响应 Body ─────────│ ← timeout 计时结束
│◄──── 响应 ────│ │
│ │ │
┌───┴───┐
│超时到期│ → 返回 504
└───────┘

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
2
3
4
5
6
7
# Nginx
proxy_connect_timeout 10s;

# Envoy
clusters:
- name: my_service
connect_timeout: 10s

差异点

  • Nginxproxy_connect_timeout 默认值为 60s,且仅适用于 HTTP upstream 模块。
  • EnvoyconnectTimeout 没有默认值,必须显式配置,否则使用全局默认(通常为 5s)。
  • Envoy 在连接超时后可结合 retryPolicy 自动重试;Nginx 需要额外配置 proxy_next_upstream timeout 才能实现类似效果。[1]

连接保活阶段:idleTimeout vs keepalive_timeout

这是两者差异最大的地方,需要重点区分。[4]

Nginx keepalive_timeout

1
2
3
4
5
6
7
8
9
# 控制 Client → Nginx 这段下游连接的保活时间
keepalive_timeout 65s;

# 控制 Nginx → Upstream 连接池的保活时间(需开启 keepalive)
upstream backend {
server 127.0.0.1:8080;
keepalive 32; # 开启连接池
keepalive_timeout 60s; # 上游连接空闲超时
}

Envoy idleTimeout(两个方向分开配置)

1
2
3
4
5
6
7
8
9
10
# 下游连接(HCM 级别)
http_connection_manager:
common_http_protocol_options:
idle_timeout: 60s # 对应 Nginx keepalive_timeout(下游侧)

# 上游连接(Cluster 级别)
clusters:
- name: my_service
common_http_protocol_options:
idle_timeout: 90s # 对应 Nginx upstream keepalive_timeout(上游侧)

核心差异

维度 Nginx keepalive_timeout Envoy idleTimeout
下游/上游 同一个指令,语境不同 明确拆分为两个参数
HTTP/2 支持 有限(需额外模块) 原生支持,发送 GOAWAY 帧
默认值 65s(下游)/ 需显式配置(上游) 1h(上下游均为 1h)
连接池集成 需手动开启 keepalive 指令 连接池原生内置,自动管理

数据传输阶段:streamIdleTimeout vs proxy_read_timeout

这两个参数最容易被混淆,但语义有微妙差异。[3]

Nginx proxy_read_timeout

1
2
# 两次连续读操作之间的最大间隔,不是请求总时长
proxy_read_timeout 60s;

关键proxy_read_timeout两次读操作之间的间隔超时,每次收到数据都会重置计时器。

Envoy streamIdleTimeout

1
2
http_connection_manager:
stream_idle_timeout: 5m # Stream 上无任何数据传输则超时

关键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
2
3
4
proxy_connect_timeout 10s;   # TCP 握手
proxy_send_timeout 30s; # 发送请求体的间隔超时
proxy_read_timeout 60s; # 读取响应的间隔超时
# 三者叠加 ≈ 请求总时长上限(但并非严格的总时长)

Envoy Route timeout 是严格的总时长

1
2
3
4
5
6
routes:
- match:
prefix: "/"
route:
cluster: my_service
timeout: 30s # 从转发请求到收到完整响应,严格的总时长上限
维度 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
2
3
4
# Nginx stream 模块
stream {
proxy_timeout 10m; # TCP 隧道空闲超时
}
1
2
3
4
5
# Envoy TcpProxy
filters:
- name: envoy.filters.network.tcp_proxy
typed_config:
idle_timeout: 10m

两者语义高度一致,都是 L4 字节级别的空闲超时,任意方向有字节传输即重置计时器。

核心设计哲学差异总结

Nginx 和 Envoy 在超时设计上的根本差异,源于它们的定位不同:

维度 Nginx Envoy
设计哲学 指令式,参数直观易懂 拓扑分层,精确管辖每段连接
HTTP/2 原生支持 有限 完整支持(GOAWAY、RST_STREAM)
Per-route 超时 需要 location 原生 per-route 配置
连接池管理 需手动开启 keepalive 内置连接池,自动管理
上下游超时分离 部分分离 完全分离,语义清晰
动态配置 需 reload xDS 动态下发,无需重启
可观测性 基础日志 内置 metrics、tracing、stats

迁移建议速查

如果你正在从 Nginx 迁移到 Envoy,以下是参数映射的快速参考:

1
2
3
4
5
6
7
8
9
Nginx                          →  Envoy
─────────────────────────────────────────────────────
proxy_connect_timeout 10s → cluster.connect_timeout: 10s
keepalive_timeout 65s → HCM.common_http_protocol_options.idle_timeout: 65s
upstream keepalive_timeout 60s → cluster.common_http_protocol_options.idle_timeout: 60s
proxy_read_timeout 60s → stream_idle_timeout: 60s (语义近似,非完全等价)
proxy_send_timeout 30s → stream_idle_timeout 涵盖双向 (合并处理)
proxy_connect+read+send 组合 → route.timeout: Xs (严格总时长,更精确)
stream { proxy_timeout 10m } → TcpProxy.idle_timeout: 10m

💡 总结

Envoy 的超时体系按连接拓扑分层设计,每个参数都有其精确的管辖边界:

  • connectTimeout → 最早,只管 TCP 握手阶段
  • HCM idleTimeout → 管下游 TCP 连接的空闲回收
  • Cluster idleTimeout → 管上游 TCP 连接的空闲回收(连接池复用)
  • streamIdleTimeout → 管单个请求 Stream 的数据流动,超时返回 408
  • Route timeout → 管请求的总端到端时长,超时返回 504
  • TcpProxy idleTimeout → L4 层整条隧道的字节级空闲超时