2. Envoy 核心架构解析:四层模型与 Worker 线程模型

Envoy 的架构设计围绕两个核心维度展开:数据平面抽象模型(四层模型)与并发执行模型(Worker 线程模型)。理解这两者是诊断延迟、优化负载均衡的基础。

2.1 四层模型:Listener → Filter → Cluster → Endpoint

Envoy 将请求处理路径抽象为四个层次,每一层都承担明确的职责,并支持通过配置进行动态调整。

层次 核心职责 延迟关键点
Listener 监听端口,接受入站连接(TCP/UDP/Unix Domain Socket)。支持 TLS 终止、代理协议解析。 连接建立耗时、TLS 握手开销、SO_REUSEPORT 争抢。
Filter 可插拔的请求/响应处理链。包括 L4 网络过滤器(如 TCP Proxy)和 L7 HTTP 过滤器(如 Router、RBAC、Fault Injection)。 过滤器链长度、每个过滤器的处理复杂度、编解码开销。
Cluster 定义上游服务集群的配置,包括连接池、健康检查、负载均衡策略、断路器、异常检测。 健康检查频率、负载均衡算法选择、连接池管理。
Endpoint 具体的上游实例(IP:Port)。由服务发现(EDS/STRICT_DNS/STATIC)动态维护。 端点变更推送延迟、端点权重分布、异常驱逐状态。
延迟诊断提示: 当出现尾部延迟(Tail Latency)时,优先检查 Cluster 层的负载均衡策略是否与后端性能分布匹配,以及 Endpoint 层是否存在被异常检测标记为 ejected 但仍被路由的实例。

下图展示了四层模型在请求处理路径中的串联关系:

Envoy 四层模型请求处理路径 Listener 端口监听 / TLS 终止 Filter 过滤器链处理 Cluster 连接池 / 负载均衡 Endpoint 上游实例 关键说明: 1. 请求从 Listener 进入,经过 Filter 链处理后,由 Router Filter 决定目标 Cluster。 2. Cluster 根据负载均衡策略选择一个 Endpoint,并通过连接池发送请求。 3. 响应路径反向经过 Filter 链,最终由 Listener 返回客户端。 4. 延迟可能发生在任意一层:TLS 握手、Filter 处理、连接池等待、负载均衡决策。

2.2 Worker 线程模型:从主线程到工作线程

Envoy 采用 多线程 + 非阻塞 I/O 架构,核心是 Worker 线程池。每个 Worker 线程独立运行一个事件循环(Event Loop),处理一组连接的所有事件。

  • 主线程(Main Thread):负责配置加载、证书轮换、统计刷新、服务发现更新。不处理数据平面请求。
  • Worker 线程:每个 Worker 拥有独立的 Event Base 和 连接池。连接通过 accept 后绑定到固定 Worker,后续所有 I/O 都在该 Worker 内完成。
  • 文件刷新线程(File Flusher):异步将访问日志写入磁盘,避免阻塞 Worker。
性能关键点: Worker 线程之间不共享连接,因此不存在锁竞争。但这也意味着连接分配不均可能导致某些 Worker 过载,而其他 Worker 空闲。使用 --concurrency 参数控制 Worker 数量,通常设置为 CPU 核心数。

下图展示了 Worker 线程模型的核心结构:

Envoy Worker 线程模型 主线程 (Main Thread) 配置 / 证书 / 服务发现 Worker 线程 1 Event Loop + 连接池 Worker 线程 2 Event Loop + 连接池 Worker 线程 N 连接 1 (绑定到 Worker 1) 连接 2 (绑定到 Worker 2) 连接 M (绑定到 Worker N) 线程模型要点 • 每个 Worker 独立运行 • 连接绑定后不迁移 • 无锁竞争设计 • 连接池按 Worker 隔离 • 主线程不处理数据 • 配置热更新通过 postAction 分发 • 日志异步写入 • 统计计数器无锁 • 推荐 concurrency = CPU 核数

2.3 四层模型与 Worker 模型的协同

当请求到达时,四层模型与 Worker 模型按以下步骤协同工作:

  1. Listener 接收:主线程将监听 socket 设置为 SO_REUSEPORT,多个 Worker 可以同时 accept 连接。连接被某个 Worker 接受后,该 Worker 负责后续所有处理。
  2. Filter 链处理:Worker 线程在事件循环中依次执行 Listener Filter、Network Filter、HTTP Filter。每个 Filter 都是非阻塞的,通过回调驱动。
  3. Cluster 路由:Router Filter 根据请求头匹配路由规则,确定目标 Cluster。Worker 线程从本地缓存的路由表读取,无需跨线程同步。
  4. Endpoint 选择:Worker 线程在本地 Cluster 对象上执行负载均衡算法(如 EDF 加权轮询、Maglev 一致性哈希),选择一个 Endpoint。
  5. 连接池复用:Worker 线程从自己的连接池中获取或创建到目标 Endpoint 的连接。连接池按 (Cluster, Endpoint, 协议) 维度隔离。
常见延迟陷阱: 当 Worker 数量超过 CPU 核心数时,线程上下文切换开销会显著增加 P99 延迟。此外,如果某个 Worker 上的连接池耗尽(如 HTTP/2 流控导致连接阻塞),该 Worker 上的所有请求都会排队等待,形成 线头阻塞(Head-of-Line Blocking)。

2.4 关键配置示例

以下是一个简化的 Envoy 配置片段,展示了四层模型与 Worker 模型的对应关系:

static_resources:
  listeners:
  - name: ingress_listener
    address:
      socket_address: { address: 0.0.0.0, port_value: 8080 }
    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:
            virtual_hosts:
            - name: backend
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route: { cluster: backend_cluster }
          http_filters:
          - name: envoy.filters.http.router

  clusters:
  - name: backend_cluster
    type: STRICT_DNS
    lb_policy: LEAST_REQUEST
    connect_timeout: 0.25s
    per_connection_buffer_limit_bytes: 32768
    load_assignment:
      cluster_name: backend_cluster
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address: { address: backend-svc, port_value: 8080 }

# Worker 线程数通过命令行参数设置
# --concurrency 4
调优建议: 在延迟敏感场景下,将 connect_timeout 设置为 100-250ms,并启用 circuit_breakers 防止级联故障。同时,使用 per_connection_buffer_limit_bytes 控制每个连接的缓冲区大小,避免内存浪费。