bigywater.tech
微服务 · 85

Envoy 的过滤器链

Envoy 的过滤器链是怎么工作的?顺序会改变结果吗?router 为什么必须在最后?怎么让某条路由不被限流?每多一个过滤器要多花多少延迟?这篇用 Envoy 1.39.3 逐项量出来。

98 vs 49
故障注入在前与限流在前时,通过的(200)请求数(429 分别是 99 和 301)
300 / 300 vs 19 / 300
/health(路由级覆盖)与 /api(过滤器级配置)通过限流的请求数
+51 µs
router 之前多了 20 个简单 Lua 过滤器后,p50 延迟相对没有 Lua 过滤器的增加

顺序

实测 · 本机执行输出
  过滤器顺序                               | 200 | 429(被限流) | 503(被故障注入中止) | 合计 
  ------------------------------------+-----+----------+--------------+----
  (a)fault → local_ratelimit → router | 98  | 99       | 203          | 400
  (b)local_ratelimit → fault → router | 49  | 301      | 50           | 400
  请求按顺序依次经过每个过滤器,谁先处理就先作用:故障注入在前时,被中止的请求(503)根本没有走到限流,不消耗令牌;限流在前时所有请求(包括之后会被中止的)都先消耗令牌,所以令牌更早用完,429 更多、通过的请求更少

请求按顺序经过每个过滤器,先处理的先起作用;故障注入和限流谁在前会改变通过的请求数。

router 在最后

实测 · 本机执行输出
  启动结果:][critical][main] [source/server/server.cc:453] error `Error: terminal filter named envoy.filters.http.router of type envoy.filters.http.router must be the last filter in a http filter chain.` initializing config 'goo.gle/debugstr | Error: terminal filter named envoy.filters.http.router of type envoy.filters.http.router must be the last filter in a http filter chain.
  router 是 “终结过滤器”(它把请求转给上游并结束过滤器链),它后面不能再有别的过滤器;Envoy 在加载配置时就检查并拒绝

router 是终结过滤器,后面不能再有别的过滤器,Envoy 在加载配置时就拒绝。

路由级覆盖

实测 · 本机执行输出
  路径                      | 200 | 429
  ------------------------+-----+----
  /api(用过滤器级配置:5 个/秒)     | 19  | 281
  /health(路由级覆盖:1000 个/秒) | 300 | 0  
  路由级的 typed_per_filter_config 覆盖了过滤器自己的配置:同一个过滤器在不同路由上可以有不同的限流参数,健康检查之类的路径可以不被限流

typed_per_filter_config 让同一个过滤器在不同路由上用不同的参数。

过滤器开销

实测 · 本机执行输出
  router 之前的 Lua 过滤器个数 | 延迟 p50(ms) | 延迟 p99(ms) | p50 相对没有 Lua 过滤器多出的微秒
  ---------------------+------------+------------+----------------------
  0                    | 0.158      | 0.256      | +0                   
  5                    | 0.166      | 0.282      | +8                   
  20                   | 0.209      | 0.651      | +51                  
  过滤器链里每多一个过滤器,每个请求都要多经过一次;Lua 过滤器要进入脚本虚拟机,开销比纯 C++ 的过滤器大。这里测的是一个很简单的脚本,真实的脚本越复杂开销越大
ERRORS {}

每个过滤器都有成本,Lua 过滤器更贵;20 个简单脚本让 p50 多 51 µs。

分步演示

Envoy 的过滤器链
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Go / nginx / Envoy / etcd / Consul / Nacos / OpenTelemetry / Prometheus(按各篇注明用到哪些及版本);所有服务都是本机自己起的进程,端口 9150 以上,只监听 127.0.0.1,不连任何外部服务,不碰本机已有的实例;故障用 kill -9 进程、让连接断开或在代码里注入延迟与错误来制造;计时用 time.perf_counter,数字每次运行会有小幅抖动。Envoy 1.39.3,--concurrency 1;后端是本机的 Python 服务,只返回一个小响应;限流用的是 local_ratelimit(本地令牌桶),故障注入用的是 fault 过滤器的 abort;第一节的状态码计数每种只跑了一次(100 个/秒的开环负载),令牌恰好用完的时刻有几个请求的出入;第四节只测了很简单的 Lua 脚本
    • 没有验证:ext_authz、jwt_authn、wasm 等其他过滤器的开销;全局限流(rate limit service);过滤器链在监听器级别的匹配(filter_chain_match,按 SNI、源地址选链);请求头 / 响应头的处理顺序(decode / encode 方向对称);Lua 过滤器的错误处理和超时
    京ICP备2026067502号-1京公网安备11010502063487号