从应用的上游地址移到代理监听端口

此前 orders 直连 inventory-v1。第 02 篇说明 Service 地址、端点和连接并不等价。若把 orders 的库存 URL 指向本地 Envoy listener,代理必须回答两个问题:收到一个 HTTP 请求时匹配哪条路由?挑选哪个实际后端建立上游连接?本篇先用独立 Envoy 进程解释这些步骤;它没有 Pod 注入,也没有 Istiod 或 xDS 下发。

Envoy 从 listener 经 filter chain、HTTP 路由到 cluster 与 endpoint 的路径

图上方的监听地址接收下游连接,图右侧的两个 inventory 进程是候选上游。业务请求并非每次都到后端:没有匹配路由可以由代理直接返回错误,选中 cluster 但后端不可连接则失败在建立上游连接阶段。用同一个 HTTP 状态码反推故障层级会丢失信息。

将五层选择映射到一次请求

  1. Listener 接受指定地址与端口上的连接;这是“谁能够连上代理”,还没选业务后端。多个 listener 或传输套接字会改变入口形态。
  2. Filter chain 根据连接属性选择网络过滤器链;可以有 TLS 处理与匹配条件。本篇明文 HTTP 样例只有一条过滤器链,不推论 SNI、ALPN 或 mTLS 的默认行为。
  3. HTTP connection manager 与 route 解析 HTTP 请求,并按 virtual host、路径等条件匹配 route。/quote 路由到 inventory cluster;/unavailable 路由到显式配置的不可连接 cluster;/no-route 不匹配任何路由。route 也可能返回直接响应或重定向,而非总是转发。
  4. Cluster 包含发现与连接策略,本例静态声明两个 inventory 实例,设 ROUND_ROBIN 作为负载均衡策略。它并不保证任意两个 HTTP 请求均匀地落到两个版本;长连接、并发、健康和尝试数都影响样本观察。
  5. 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 的行为。

练习与参考解答

  1. 一个请求在 Envoy 日志里有 upstream_host,却在该库存实例日志里没有对应 request ID。能否宣布应用已处理?参考解答:不能。地址被选中不等于连接成功或应用读到完整请求;核对代理的响应标记/详情与上游连接失败事件,按相同标识搜索其他库存实例日志。若缺乏请求标识关联,应先补证据,不从 cluster 配置推导执行结果。
  2. 将 /quote 路由的 cluster 名改为不存在的名字,或把两个 endpoint 都指向未监听端口,预期失败层级有何差异?参考解答:不存在的 cluster 引用可能在 --mode validate 或配置接受阶段被拒,必须以固定版本验证错误为准;不可连接端口的配置可能能通过校验,但运行时建立上游连接失败。一个是配置/依赖错误,一个是已接受配置下的实际连接错误,不能用“配置已下发”同时证明两者成功。

官方资料与系列导航

系列导航:00 一次服务调用需要哪些基础条件 → 01 SDK、网关与服务网格分别处理什么 → 02 Service 地址怎样到达 Pod → 03 Envoy 怎样转发请求(本文)→ 04 Sidecar 怎样接管流量。