🔍 登录 注册

Envoy 数据面路由与加权流量切分实战

Z ZeroIsStart 2026-10-23 👁 0 ⏱ 7min

Envoy 数据面路由与加权流量切分实战

Envoy 常被当作"Istio 的 sidecar"来认识,但它本体是一个通用 L7 代理:路由、加权分流、重试、熔断、可观测性都在它自己身上。理解 Envoy 的路由模型,比理解任何 Service Mesh 抽象都更底层、更耐用。本文按 Listener、Route、Cluster、Endpoint 四层展开。

一、四层模型与两个关键语义

Listener(监听端口)
  → Filter Chain(HCM 等网络过滤器)
    → RouteConfiguration(虚拟主机与路由规则)
      → Cluster(逻辑服务及其 Endpoint)

两个最容易被忽略的语义:

  1. Cluster 是逻辑服务定义(LB 策略、熔断、健康检查、连接池),Route 只负责如何选 Cluster(匹配条件加目标);
  2. 路由匹配是顺序优先:第一条完全匹配即生效,不是"最具体优先"。规则顺序写错,后面的规则永远命中不了。

一个可用的静态骨架:

static_resources:
  listeners:
  - name: http
    address: { socket_address: { address: 0.0.0.0, port_value: 10000 } }
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: api
              domains: ["api.okgoes.com", "api.okgoes.com:10000"]
              routes:
              - match: { prefix: "/api/v1/articles" }
                route: { cluster: article-svc, timeout: 2s }
              - match: { prefix: "/" }
                route: { cluster: web-svc, timeout: 5s }
          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
  clusters:
  - name: article-svc
    type: STRICT_DNS
    lb_policy: LEAST_REQUEST
    load_assignment:
      cluster_name: article-svc
      endpoints:
      - lb_endpoints:
        - endpoint: { address: { socket_address: { address: article-svc, port_value: 8080 } } }

stat_prefix 是所有指标名的前缀,一旦上报就别再改,否则看板全断。

二、加权流量切分的四种写法

其一,Endpoint 权重,用于同服务不同版本之间分流:

load_assignment:
  cluster_name: article-svc
  endpoints:
  - lb_endpoints: [{ endpoint: { address: {...v1...} }, weight: 90 }]
  - lb_endpoints: [{ endpoint: { address: {...v2...} }, weight: 10 }]

其二,Route 级 weighted_clusters,跨服务 A/B 最直观,还能顺手打实验标:

routes:
- match: { prefix: "/api/v1/search" }
  route:
    weighted_clusters:
      total_weight: 100
      clusters:
      - name: search-v1
        weight: 95
      - name: search-v2
        weight: 5
        request_headers_to_add:
        - { header: { key: "x-experiment", value: "search-v2" }, append: false }

其三,Header 定向,让内部账号先体验新版,规则必须放在通用规则之前:

routes:
- match:
    prefix: "/api/v1/search"
    headers:
    - name: x-user-id
      string_match: { safe_regex: { regex: "^(10[0-9]{8})$" } }
  route: { cluster: search-v2 }
- match: { prefix: "/api/v1/search" }
  route: { cluster: search-v1 }

其四,一致性分流。纯随机加权会让同一用户在两个版本之间来回跳,体验割裂。要么用一致性哈希按用户主体分流:

lb_policy: RING_HASH
hash_policy:
- header: { name: "x-user-id" }

要么下发会话标识并由上游保证粘性。做实验分流时,"同一个人始终看到同一个版本"几乎总是必需项。

三、重试与超时:默认值几乎都不对

Envoy 默认路由超时 15 秒、无重试、无熔断。生产最少要设这三样:

routes:
- match: { prefix: "/api/v1/" }
  route:
    cluster: backend
    timeout: 1s
    idle_timeout: 3s
    retry_policy:
      retry_on: 5xx,reset,retriable-status-codes
      num_retries: 2
      per_try_timeout: 300ms
      retry_back_off: { base_interval: 25ms, max_interval: 250ms }
      retriable_status_codes: [503]

三个陷阱:

  1. timeout 是整条请求上限,per_try_timeout 是单次尝试上限。timeout: 1s 配 per_try_timeout: 800ms 再重试 2 次,第二次永远活不过 200ms,重试形同虚设;
  2. 重试只对幂等操作安全,默认 retry_on 不含 POST;若要重试非幂等写请求,必须业务侧确认;
  3. 5xx 与 reset 类重试会在下游过载时放大流量,形成重试风暴,因此必须与熔断配对。

四、三层保护各司其职

- name: backend
  type: STRICT_DNS
  connect_timeout: 0.25s
  lb_policy: LEAST_REQUEST
  circuit_breakers:
    thresholds:
    - priority: DEFAULT
      max_connections: 200
      max_pending_requests: 100
      max_requests: 400
      max_retries: 2
  outlier_detection:
    consecutive_5xx: 5
    interval: 10s
    base_ejection_time: 30s
    max_ejection_percent: 50

连接池上限保护下游资源,outlier detection 踢掉持续报错的实例,circuit breaker 在本地快速失败。三者职责不同,不是重复配置。max_retries 单独限制重试并发,是防重试风暴最有效的一层。

五、观测重点与诊断字段

envoy_cluster_upstream_cx_active
envoy_cluster_upstream_rq_per_try_timeout
envoy_cluster_upstream_rq_retry / envoy_cluster_upstream_rq_retry_success
envoy_cluster_upstream_cx_pool_overflow
envoy_cluster_upstream_circuit_breakers_default_remaining

三条常用判断:rq_retry / rq_total 偏高说明下游在抖动,先查下游而不是改路由;cx_pool_overflow 增长说明触达连接池上限;rq_per_try_timeout 高说明单次超时设得过小。

访问日志里务必打出 %RESPONSE_FLAGS%,这是 Envoy 独有的诊断字段:UT 上游超时、UF 连接失败、UH 无健康主机、LR 本地限速、DC 下游断连。排查"这个 502 到底是谁的锅"基本就靠它。

六、xDS 与热更新的三条纪律

生产要用动态配置(LDS、RDS、CDS、EDS)而非静态文件,否则会失去健康检查与增量更新能力。三条纪律:

  1. 不要通过重启 Envoy 来改流量。xDS 推送是原子的,重启会打断长连接;
  2. drain_time 要给足,滚动升级时保留旧 listener 一段时间让连接自然结束,否则表现为零星 502;
  3. 配置变更必须灰度。xDS 控制面要有 dry-run 与回滚能力,一份写错的 RDS 能让全站瞬间"无健康主机"。
envoy --mode validate -c /etc/envoy/envoy.yaml      # 上线前必做
curl -X POST http://127.0.0.1:9901/reload           # 静态配置场景热加载

小结

Envoy 的心智模型是路由决定选哪个集群,集群决定怎么连,再叠上路由匹配的顺序优先语义,就理解了八成配置能力。流量切分按"是否跨服务"在 endpoint weight 与 weighted_clusters 之间选择,并确定一致性分流策略。生产配置最少四件事都要做:路由 timeout 与 per_try_timeout 匹配、重试配熔断、连接池上限与 outlier 一起上、访问日志带 %RESPONSE_FLAGS%。守住这三层保护与一条诊断字段,Envoy 才真正担得起入口职责,而不是一个会转发的黑盒。

读完了?给作者一点支持

本文采用 CC BY 4.0 协议转载 · © ZeroIsStart

评论(0)

登录 后参与评论。

  • 还没有评论,来抢沙发。

🔗 相关文章