Envoy Go HTTP Filter

Go HTTP 过滤器 (envoy.filters.http.golang) 允许使用 Go 编写 Envoy HTTP 过滤器。Go 代码被编译为共享库 (.so),在运行时通过 dlopen 加载,并借由 CGo bridge 集成到 Envoy 的 C++ 过滤器链中。

架构

Go HTTP Filter 主要分为两端:

  • C++ 端:负责 DSO 加载、过滤器链集成以及线程安全。
  • Go 端:通过 Go SDK 实现过滤器逻辑,通过 CGo 与 C++ 通信。

ABI 边界由 contrib/golang/common/go/api/api.h 中的共享 C 结构体定义。

1d4a997271052a5a6344e5235da18b13_MD5

DSO (动态共享对象) 加载机制

Go HTTP 过滤器通过 contrib/golang/common/dso/ 中定义的专用 DSO 机制加载 Go 编译的共享库。这与 Envoy 核心扩展中的 dynamic_modules 是两套独立的实现。

核心组件

1. DSO 管理器 (DsoManager)

DsoManager 类 (contrib/golang/common/dso/dso.h) 负责管理共享库的生命周期:

nano-vllm 代码流程

整体架构图

┌─────────────────────────────────────────────────────────────────┐
│                         用户入口层                               │
│  example.py → LLM.generate() → add_request() + step() 循环      │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                         引擎层 (Engine)                          │
│  ┌─────────────┐   ┌─────────────┐   ┌──────────────────┐      │
│  │  Scheduler  │ → │ ModelRunner │ → │  BlockManager    │      │
│  │  (调度器)    │   │ (模型执行器) │   │  (KV Cache管理)  │      │
│  └─────────────┘   └─────────────┘   └──────────────────┘      │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                         模型层 (Models)                          │
│  Qwen3ForCausalLM → Qwen3Model → [Qwen3DecoderLayer x N]       │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                         算子层 (Layers)                          │
│  Attention │ Linear │ LayerNorm │ RoPE │ Activation │ Sampler  │
└─────────────────────────────────────────────────────────────────┘

推理服务流程详解

阶段 1: 初始化阶段

# example.py
llm = LLM(path, enforce_eager=True, tensor_parallel_size=1)

调用链:

时延的建模与测量

本文是 2025年10月出版的《Latency: Reduce delay in software systems》第二章的读书笔记

时延的定律

Little’s Law

利特尔法则(Little’s Law)是排队论和运筹学中最经典、最直观,但也最具威力的定律之一。

Little’s Law 的数学表达式如下所示: $$L = \lambda \times W $$

  • $L$ (Inventory / Queue Length):系统中平均拥有的“东西”数量(比如排队的人数、仓库的库存、处理的任务)
  • $\lambda$ (Throughput / Arrival Rate):单位时间内进入或离开系统的平均数量(吞吐率/到达率)
  • $W$ (Wait Time / Cycle Time):一个“东西”在系统里停留的平均时间(前置时间/等待时间)

需要注意的是,Little’s Law 描述的是一个稳态系统:在一个稳定的系统中,存货数量 = 到达速率 x 停留时间

举个例子,一个咖啡馆内,平均每分钟有 2 个客人进店($\lambda$),每个客人在店里从进门到拿咖啡走人平均停留 10 分钟($W$)。那么,任一时刻店中的停留人数 $L = \lambda \times W$ 为 20 人,即在这 10 分钟内店中累积的人数,第 11 分钟到达速率等于离开速率,系统达到平衡。

在并发系统中,$\lambda$ (吞吐量) 可以理解为每秒处理多少个请求(QPS/TPS),$W$ (响应时间) 可以理解为每个请求平均花多少秒(Latency),$L$ (并发数) 可以理解为在任一给定时刻,已经进入系统但尚未处理完成的请求总数。

在计算机系统中,每一个 $L$ 都不是免费的,它必须占用某种物理资源:

KubeCon North America 2025 Review

KubeCon North America 2025 13号结束了,官网上也有了些会议资料。挑了几个感兴趣的话题总结下。

Dynamic Routing with Multi-Cluster Inference Gateway

https://kccncna2025.sched.com/event/27FeP/ai-inference-without-boundaries-dynamic-routing-with-multi-cluster-inference-gateway-rob-scott-google-daneyon-hansen-soloio?iframe=no&w=100%&sidebar=yes&bg=no

这段时间正好在做 AI 网关,这个话题可以说是“瞌睡了送枕头”。

推理服务和传统 API 流量相比,在 payload,响应时间,后端资源开销上都有着很大的差异(见下图)。 2781cd595fe30c8da311d124a8e4ee35_MD5

因此,推理网关需要做到后端负载感知的调度(传统 API 网关也有类似的方案,尤其是在后端机型不一样,普通的 rr 无法均匀负载时,做后端负载感知动态调权)。在 Gateway 和推理实例间引入了一个 EPP(Endpoint Picker)组件(注:EPP 现在也是 Kubernetes 做推理服务的一个通用组件),采集推理实例的指标来动态选择推理后端。 3462773fb295a9dabdabc886b1de43ca_MD5

benchmark 数据显示,使用推理网关相比传统负载均衡,推理实例间的负载更加均衡,请求排队更少,从而降低了响应时间。

在多集群场景下,这套方案需要解决 3 个问题:

  1. 服务发现:Cluster Inference Services 如何暴露给 Gateway?
  2. 后端选择:Gateway 如何在多集群间分配流量?
  3. 路由模式:流量如何从 Gateway 转发到集群?

第一个问题作者提了 3 个解决方法: fd7a6ffacbfd58fcd6482dd40b861461_MD5

不是关注重点,略过。

第二个问题,简单的 RR 和 Active-Passive 肯定就失去了推理网关负载感知的优势。所以,在 EPP 感知负载之外,Gateway 也得做负载感知。作者也提了两个方法: 1d3df4bac299f39b0c917b886bab2a27_MD5 f4bd518bc6dce2999d5805d5b2d46dac_MD5

从层级上来说,EPP Aggregate Metrics 方案更加简洁,毕竟在 EPP 上还得做二次调度。

最后一个问题,如果 EPP 能跨集群直接访问,direct routing 是最合适的方式,不行的话再加一层网关,使用 Cluster-Local Gateway 做暴露也能访问。

网关的限流方案

Note:本文由 Google Gemini DeepResearch 生成。

1.网关限流服务的战略必要性与流量控制架构

1.1. 网关在流量管理中的控制点角色与核心必要性

API 网关在现代微服务架构中扮演着至关重要的流量控制点角色,负责管理北向(Ingress)和南向(Egress)的流量。对流量实施限制(即限流)是保障上游服务稳定性、维持系统可用性和实现资源公平分配的核心防御机制。

限流服务的战略必要性主要体现在以下几个方面:

首先,限流是防止系统过载和雪崩效应的关键。当面临突发流量高峰时,如不加限制,后台服务器可能因资源耗尽而被压垮。通过限制客户端在指定时间窗内的请求次数,网关可以有效地保护应用编程接口(API)及其背后的微服务。其次,限流是抵御恶意攻击的有效手段,尤其是分布式拒绝服务攻击(DDoS)或防止未受限制的网络爬虫过度消耗计算资源。如果任由客户端无限制地访问上游服务,系统的可用性将受到负面影响。

从架构设计的角度看,网关作为请求进入系统的唯一入口,是实施全局限流策略的最佳位置,可以根据 IP、API 密钥、特定消费者(Consumer)或其他定制标准来应用限流规则。

1.2. 限流策略的粒度、层次结构与定制化响应

在实际应用中,灵活的限流服务需要支持精细的粒度和层次化配置模型,以满足不同业务和资源保护的需求。这种配置的优先级机制体现了一种 “微观精确控制”优于“宏观默认保护” 的设计哲学。

网关通常支持多层次的限流配置,包括:

  1. 特定 API 或消费者限流: 这是优先级最高的限流配置。例如,用户可以对单个关键 API 进行限流设置,或者对特定的消费者群体施加限制。这种配置会覆盖所有默认设置。这意味着在限流值冲突时,系统优先执行最明确定义或最严格的限制,确保关键资源的防护级别不受宏观策略的稀释。
  2. API 默认限流: 作用于应用下大部分 API 的通用配置。当没有针对单个 API 配置限流值时,将采用此默认值。
  3. 应用总限流 (App Total Limit): 这是对当前应用下所有 API 请求的总和设置的上限。应用总限流是防止资源被“虹吸”的最后防线。即使单个 API 的限流都没有达到,一旦应用的总请求量超出限流总和值,所有请求都将被限制。这种设计解决了在微服务架构中,攻击者通过对大量低 QPS API 进行频繁调用,最终耗尽整个应用共享资源预算的“长尾攻击”问题。

网关在触发限流时,必须向客户端返回明确的、用户友好的响应。例如,默认的限流响应可能包含 {"resultStatus":1002, "tips":"顾客太多,客官请稍候"} 。为了实现友好的降级体验,网关需要支持定制化的响应格式,允许用户配置 JSON 格式的返回内容,如通过 resultStatus 字段(例如,1002 表示限流)和 tips 字段向用户提供定制化的限流提示。

2. 常见限流算法的原理、适用性与分布式性能剖析

选择合适的限流算法是构建高性能网关服务的基石。不同的算法在精确度、突发处理能力和分布式系统中的扩展性方面存在显著差异。

2.1. 固定窗口计数器 (Fixed Window Counter, FWC)

固定窗口计数器是最简单、资源消耗最低的限流算法。它在一个固定的时间周期内(例如每 60 秒)累积请求计数,当周期结束时,计数器重置。

优势与局限性: FWC 的实现非常简单,查询和递增操作计算复杂度为 $O(1)$,因此延迟极低。然而,其主要局限在于边界突发问题。如果客户端在窗口结束前的一刻发出大量请求,并在下一个窗口开始后的第一时间再次发出同等量的请求,系统在短时间内实际放行的请求量可能高达阈值的两倍。这使得 FWC 难以应对流量的瞬间爆发,可能导致上游服务过载。