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 难以应对流量的瞬间爆发,可能导致上游服务过载。

常见的大模型 OpenAPI 规范

AI Gateway 的对接开发中,一个重要的内容就是对接不同厂商推理服务的接口协议。目前,推理服务的接口协议主要分为以下几种类型:

  • 文本对话接口,如 OpenAI 的 chat completions 和 response API 等
  • 向量接口,向量接口用于将输入的文本或者图片、视频(多模态)等转换成向量表示,适用于搜索(文搜图、图搜图、图文混合搜索)、聚类、推荐等场景。

文本对话

文本对话 API 需要提供如下能力支持:

  1. 模型选择
  2. 用户、系统、模型输入内容角色区分
  3. 模型参数调整
  4. 工具调用
  5. MCP 支持
  6. 用量统计

目前使用最广泛的文本对话接口自然是 OpenAI 的 chat completions API。几乎所有的 LLM 服务提供商都支持 chat completions compatible 调用。

https://platform.openai.com/docs/api-reference/introduction

chat completions

基本调用:

curl https://api.openai.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-5",
    "messages": [
      {
        "role": "developer",
        "content": "You are a helpful assistant."
      },
      {
        "role": "user",
        "content": "Hello!"
      }
    ]
  }'
  • model: 请求调用的模型
  • messages:构成对话的消息体。根据消息的来源角色,message 可以分为 developer/system(开发者,系统提供的 prompt),user(用户自身的输入)和 assistant(模型的响应,用于多轮对话时模型的上下文传递)。
    • content 分为两种类型,纯文本即为 string,非纯文本 content 为列表类型,内容根据 type(text,image_url,input_audio 等)有不同的字段
  • stream:采用正常 HTTP 响应还是 sse 响应
  • stream_options: 在 sse 响应时,有些模型默认不会输出 usage 信息,需要显式将 stream_options.include_usage 设置成 true
  • temperature:模型温度,取值范围 0 - 2,值越高,输出的 tokens 随机性越大
  • top_p:和 tempreature 一样对模型输出进行调整的参数,模型会考虑概率质量最高的top_p个tokens的结果。所以0.1意味着只考虑概率质量最高的10%的tokens。
  • reasoning_effort:模型的推理深度,比如对于 OpenAI 模型来说有 minimal,low,medium,high 等多种选择。
  • max_completion_tokens:最大输出 tokens 数,包括 output tokens 和 reasoning tokens。替代原来的 max_tokens 字段。
  • tools:告知大模型本地可调用的工具列表。工具里面定义了工具的名称、描述和 json schema 表示的参数描述。替代原来的 functions 字段。
  • tool_choice:告知模型对于工具的调用选择。none 表示不要调用任何工具直接生成 messages,auto 表示由模型自己决定是否调用 allowed_tools。required 表示模型必须调用至少一个工具。

除此之外,各家可以在 OpenAI 标准的 API 上扩展自己的字段,比如 cherry studio 会使用 thinking 字段来开启/关闭模型思考: