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 但仍被路由的实例。
下图展示了四层模型在请求处理路径中的串联关系:
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 线程模型的核心结构:
2.3 四层模型与 Worker 模型的协同
当请求到达时,四层模型与 Worker 模型按以下步骤协同工作:
- Listener 接收:主线程将监听 socket 设置为
SO_REUSEPORT,多个 Worker 可以同时 accept 连接。连接被某个 Worker 接受后,该 Worker 负责后续所有处理。 - Filter 链处理:Worker 线程在事件循环中依次执行 Listener Filter、Network Filter、HTTP Filter。每个 Filter 都是非阻塞的,通过回调驱动。
- Cluster 路由:Router Filter 根据请求头匹配路由规则,确定目标 Cluster。Worker 线程从本地缓存的路由表读取,无需跨线程同步。
- Endpoint 选择:Worker 线程在本地 Cluster 对象上执行负载均衡算法(如 EDF 加权轮询、Maglev 一致性哈希),选择一个 Endpoint。
- 连接池复用: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 控制每个连接的缓冲区大小,避免内存浪费。