服务网格 03:Envoy 怎样转发请求
从应用的上游地址移到代理监听端口
此前 orders 直连 inventory-v1。第 02 篇说明 Service 地址、端点和连接并不等价。若把 orders 的库存 URL 指向本地 Envoy listener,代理必须回答两个问题:收到一个 HTTP 请求时匹配哪条路由?挑选哪个实际后端建立上游连接?本篇先用独立 Envoy 进程解释这些步骤;它没有 Pod 注入,也没有 Istiod 或 xDS 下发。
图上方的监听地址接收下游连接,图右侧的两个 inventory 进程是候选上游。业务请求并非每次都到后端:没有匹配路由可以由代理直接返回错误,选中 cluster 但后端不可连接则失败在建立上游连接阶段。用同一个 HTTP 状态码反推故障层级会丢失信息。
将五层选择映射到一次请求
- Listener 接受指定地址与端口上的连接;这是“谁能够连上代理”,还没选业务后端。多个 listener 或传输套接字会改变入口形态。
- Filter chain 根据连接属性选择网络过滤器链;可以有 TLS 处理与匹配条件。本篇明文 HTTP 样例只有一条过滤器链,不推论 SNI、ALPN 或 mTLS 的默认行为。
- HTTP connection manager 与 route 解析 HTTP 请求,并按 virtual host、路径等条件匹配 route。
/quote路由到inventorycluster;/unavailable路由到显式配置的不可连接 cluster;/no-route不匹配任何路由。route 也可能返回直接响应或重定向,而非总是转发。 - Cluster 包含发现与连接策略,本例静态声明两个 inventory 实例,设
ROUND_ROBIN作为负载均衡策略。它并不保证任意两个 HTTP 请求均匀地落到两个版本;长连接、并发、健康和尝试数都影响样本观察。 - Endpoint 是选中的实际地址和端口;还要通过上游连接才能读取应用响应。
/unavailable虽能匹配路由并找到配置中的 endpoint,但指向未监听的 loopback:1,因而不能得到 inventory 响应。
用户在 orders 侧只看到发往 Envoy 的请求;要证明经代理转发,至少交叉检查代理访问日志里的 X-Request-ID、upstream_host、后端 inventory 日志中的同一标识,以及最终返回的 inventory_version。config_dump 只能确认进程内配置存在,不能单独证明请求经过配置;此处配置来自静态 YAML,没有“xDS 已 ACK”的阶段。第 06–07 篇才研究控制面下发及拒绝。
用一份配置区分无路由与上游拒绝
在 examples/service-mesh/ 运行 ENVOY_BIN=/path/to/envoy bash scripts/run-03.sh,二进制需与版本卡固定的 Envoy 版本一致。脚本先执行 Envoy 配置校验,再启动合成应用及代理;渲染后的配置、完整启动命令和版本输出均保存到 evidence/03/<时间>-<PID>/。直接运行模板文件会遗漏脚本分配的端口,应使用此入口。
正常路径是 loadgen → frontend → orders → Envoy listener → HTTP connection manager → route → inventory cluster → inventory-v1/v2。正常组发送 24 个不同请求 ID,响应中的 ID 必须匹配输入,库存版本及外部依赖结果也必须正确。Envoy 访问日志独立写入 envoy-access.jsonl,避免与进程 stderr 的不同文件描述符共同覆盖日志。
负例直接访问代理:/no-route 不应产生到 inventory 的上游调用;/unavailable 则命中故意指向 loopback 端口 1 的 cluster,产生上游连接失败。分析时同时看 response_code、response_flags、upstream_host 和后端日志:两个失败都可能被客户端归为失败,却发生在不同选择阶段。不能只凭 503 猜测业务进程返回错误。
恢复使用原始正常 /quote?sku=paper 路径,并重新运行脚本验证全部场景;无需修改应用业务逻辑。退出钩子仅结束该次启动的进程。源码校验通过与真实 Envoy 接受配置、请求完成是三类证据,运行结果以本次目录的 run.exit 和 assertions 为准。
2026-10-03 Linux 实测记录在 examples/service-mesh/evidence/03/20261003T092808Z-1/,本轮运行通过;原始命令、退出码和日志限定结论适用范围。
[PATTERN] 代理把地址选择和协议处理组合起来,但业务身份与结果关联仍由应用验证;配置中的 listener、route、cluster、endpoint 是不同责任层。
| 原则 | 具体观察 |
|---|---|
| 逐层缩小故障位置 | 无路由没有上游目标,连接失败已有目标 |
| 隔离诊断通道 | 访问日志不与进程 stderr 共享输出文件 |
| 反向校验成功 | 同时核对代理 ID 与后端 ID |
反例、定位与边界
试想 /no-route 返回代理本地错误:修改库存实例的 Go 处理器不能修复 route 未匹配;应先看配置的 route 条件及 Envoy 访问日志。反过来,/unavailable 已匹配 cluster,即便能在 config_dump 看到 endpoint,连接目标端口无人监听仍然失败;应看 upstream_host、连接失败详情与后端是否有相同 request ID。这两种失败位置需要真实代理运行结果核对,静态配置只能展示意图。
若 orders 改成直连库存进程,应用响应可能与代理转发时一样,却不再经过 Envoy;单看业务字段不能证明经过代理。静态 Envoy 只能解释这个本机独立进程的路由过程,不自动推导 Kubernetes Service、sidecar 流量劫持或 ambient waypoint 的行为。
练习与参考解答
- 一个请求在 Envoy 日志里有
upstream_host,却在该库存实例日志里没有对应 request ID。能否宣布应用已处理?参考解答:不能。地址被选中不等于连接成功或应用读到完整请求;核对代理的响应标记/详情与上游连接失败事件,按相同标识搜索其他库存实例日志。若缺乏请求标识关联,应先补证据,不从 cluster 配置推导执行结果。 - 将
/quote路由的 cluster 名改为不存在的名字,或把两个 endpoint 都指向未监听端口,预期失败层级有何差异?参考解答:不存在的 cluster 引用可能在--mode validate或配置接受阶段被拒,必须以固定版本验证错误为准;不可连接端口的配置可能能通过校验,但运行时建立上游连接失败。一个是配置/依赖错误,一个是已接受配置下的实际连接错误,不能用“配置已下发”同时证明两者成功。
官方资料与系列导航
- Envoy v1.37.0 静态配置入门:配置对象与本机进程示例。
- Envoy v1.37.0 Listener 与 HTTP routing:filter chain、虚拟主机和 route 的关系。
- Envoy v1.37.0 Cluster manager 与 Endpoint API:上游候选集合与端点配置。以上链接定位到选定文档版本,运行结果仍需待实测。
系列导航:00 一次服务调用需要哪些基础条件 → 01 SDK、网关与服务网格分别处理什么 → 02 Service 地址怎样到达 Pod → 03 Envoy 怎样转发请求(本文)→ 04 Sidecar 怎样接管流量。
