AIBrix Router 架构与路由算法实战:可插拔的 LLM 推理流量调度引擎
【免费下载链接】aibrixCost-efficient and pluggable Infrastructure components for GenAI inference项目地址: https://gitcode.com/GitHub_Trending/ai/aibrix
导读
AIBrix Router 是 AIBrix LLM 推理服务栈中负责智能流量管理的核心组件,它以 Envoy Gateway 外部处理(External Processing)扩展的形式嵌入网关,作为所有 LLM 推理请求的唯一入口,屏蔽了多模型、LoRA 适配器、异构 GPU 后端与多样化扩缩容策略的底层复杂性。本文以 AIBrix Router 设计文档 为主干,结合 网关路由功能指南、生产部署指南 以及 pkg/plugins/gateway 下的源码实现,系统讲解路由器的核心原理、内置路由策略、配置方式与算法扩展方法。读完本文,你将掌握如何在 AIBrix 中按流量特征选择路由策略、通过请求头与环境变量精细控制路由行为,并能够基于Router接口开发自定义路由算法。
核心设计原则:本地缓存 + 多目标路由
AIBrix Router 与"每次请求实时查询 Pod"的朴素路由方案不同,它通过周期性拉取与订阅机制维护一份高频更新的 Pod 指标本地缓存(涵盖 pod 运行中请求数、KV cache 占用、drain rate 等),所有路由算法基于这份缓存快照做决策,而不阻塞地实时查询各 Pod。这一设计带来了两个关键收益:
- 热路径低延迟:路由决策只做内存内的指标读取与打分计算,不需要在请求关键路径上发起网络查询;
- 高 QPS 扩展性:缓存由独立组件持续刷新,网关可以轻松支撑数千 QPS 的推理流量(设计文档中明确以 "scaling to thousands of QPS" 为设计目标)。
路由策略的选择有三层优先级(从高到低):
- 每请求通过
routing-strategyHTTP 请求头指定; - 通过
ROUTING_ALGORITHM环境变量全局指定默认策略; - 通过模型配置档案(model config profile)注解为特定模型指定策略。
这条策略解析链在源码中有完整实现:RouterManager.Select(pkg/plugins/gateway/algorithms/router.go)先解析算法字符串,再按配置选取路由器实例;Validate()则在请求进入前校验策略名是否被注册,未注册的策略会返回 400 Bad Request,而不是静默回退到 random。
详细请求序列流程
设计文档给出了完整的请求流转时序,我们将其整理并补充实现佐证:
Client │ │ POST /v1/chat/completions ▼ Envoy │ │ External Processing Hook ▼ GatewayPlugin │ │ Make routing decision ▼ Router ──────────────────▶ Cache │ Query pod metrics │ │ & KV state │ │ ◀─────────────────────────┘ │ Return latest metrics │ │ Apply routing algorithm │ │ Forward request to selected pod ▼ InferencePod │ │ Return streamed tokens ▼ GatewayPlugin ──▶ Envoy ──▶ Client对应到源码:请求经 Envoy 的 ext_proc 过滤器转发给 gateway plugin(pkg/plugins/gateway/gateway.go),selectTargetPod负责解析策略、应用负载不均衡门控(ApplyLoadImbalanceGate)、随后调用路由器选出目标 Pod;RoutingContext.TargetAddress()(pkg/types/router_context.go)最终拼出IP:Port地址,供 Envoy 将请求转发到选中的推理 Pod。流式 token 沿反方向经 Envoy 回传客户端。
内置路由策略全解析
AIBRIX 提供了一套面向不同工作负载特征的内置算法,全部注册于 pkg/plugins/gateway/algorithms 目录下的init()函数中。以下按设计文档的分类逐一说明。
通用负载均衡(General Load Balancing)
| 策略名 | 行为描述 | 源码文件 |
|---|---|---|
random | 随机选择一个 Pod,适合作为基线或 Pod 同构、负载均匀的场景 | random.go |
least-request | 选择在途请求数最少的 Pod | least_request.go |
least-busy-time | 选择累计忙碌处理时间最短的 Pod | least_busy_time.go |
least-latency | 选择平均处理延迟最低的 Pod | least_latency.go |
least-kv-cache | 选择当前 KV cache 占用(VRAM 用量)最小的 Pod | least_kv_cache.go |
least-gpu-cache | 选择 GPU cache 利用率最低的 Pod | least_gpu_cache.go |
least-utilization | 选择整体利用率评分最低的 Pod | least_util.go |
load-balance | 容量感知的加权 least-request 路由,见下文详解 | load_balance.go |
throughput | 选择累计处理加权 token 数最少的 Pod,偏向负载较轻的 Pod | throughput.go |
power-of-two | 两随机选择(power-of-two choices)算法:随机采样两个 Pod 并选择更优者 | power_of_two.go |
load-balance的容量感知打分:每个 Pod 的评分定义为running_requests / drain_rate(即预计排队时间 pending time),其中drain_rate是观测到的请求完成速率。路由选择 pending time 最低的 Pod;当多个 Pod 平局时,用"最小 GPU+CPU 组合 KV-cache 用量"作为次级信号破平(若平局 Pod 的缓存指标不可用则随机选择)——这与prefix-cache用请求数来打破前缀匹配百分比平局的模式一致。当 drain-rate 指标不可用时,回退到均匀容量(drain_rate = 1)假设。实现细节见 load_balance.go。
KV-Cache 感知(KV-Cache Aware)
prefix-cache:路由到已缓存与该请求 prompt 前缀匹配的 KV cache 的 Pod,在标准差负载阈值(AIBRIX_PREFIX_CACHE_STANDARD_DEVIATION_FACTOR,默认 1)内选择前缀匹配最佳者。它纯粹关注前缀缓存命中率,自身不执行集群级负载不均衡门控(该门控由网关在路由前统一施加,见下文"负载不均衡门控"小节)。支持两种模式:- 标准模式:本地哈希表索引;
- KV 同步模式:通过实时分布式索引共享跨副本的 KV 状态,需设置
AIBRIX_PREFIX_CACHE_KV_EVENT_SYNC_ENABLED=true,同时要求AIBRIX_PREFIX_CACHE_USE_REMOTE_TOKENIZER=true与AIBRIX_PREFIX_CACHE_TOKENIZER_TYPE=remote。完整依赖关系见 pkg/plugins/gateway/ENV_VARS.md。
prefix-cache-preble:同时考虑前缀缓存命中率与 Pod 负载,基于 Preble 论文(Efficient Distributed Prompt Scheduling for LLM Serving)的思想实现,见 prefix_cache_preble.go。
公平性(Fairness)
vtc-basic:用混合评分平衡"每用户 token 公平性"与"Pod 利用率",是 Virtual Token Counter(VTC)算法的简化变体,实现在 pkg/plugins/gateway/algorithms/vtc 目录。其评分公式为score = (fairnessWeight * normFairness + utilizationWeight * normUtilization) / normFreeGPU,相关权重(如AIBRIX_ROUTER_VTC_BASIC_FAIRNESS_WEIGHT、AIBRIX_ROUTER_VTC_BASIC_UTILIZATION_WEIGHT,均默认 1.0)与输入/输出 token 权重(默认 1.0 / 2.0)均可用环境变量调优。
SLO 感知(SLO-Aware)
| 策略名 | 行为描述 |
|---|---|
slo | 感知每请求服务等级目标(SLO)的路由 |
slo-pack-load | SLO 感知且把负载打包到更少 Pod 上以提升效率 |
slo-least-load | SLO 感知且把负载分散到最空闲的 Pod |
slo-least-load-pulling | slo-least-load的变体,直接拉取实时指标而非依赖缓存快照 |
实现位于 slo.go 与 pkg/plugins/gateway/queue 目录。
专用策略(Specialized)
pd:prefill-decode 分离路由,将处理拆分为专用 prefill Pod 与 decode Pod,以优化端到端延迟。实现分散在 pd_disaggregation.go 与 pkg/plugins/gateway/algorithms/pd 子目录,支持通过AIBRIX_PROMPT_LENGTH_BUCKETING按 prompt 长度分桶、通过AIBRIX_PREFILL_SCORE_POLICY/AIBRIX_DECODE_SCORE_POLICY分别选择 prefill/decode 打分策略。session-affinity:粘性会话路由。将目标 Pod 地址(IP:Port)base64 编码进x-session-id响应头;后续携带该头的请求被路由到同一 Pod;若原 Pod 不可用,则回退到随机可用 Pod 并签发新的 session ID。注意x-session-id只编码网络位置,不是安全令牌,不能用于认证或授权。实现见 simple_session_affinity.go。
自动混合容量感知(Auto-Blended Capacity Awareness)
这是设计文档强调的一个"默认开启、无需 opt-in"的能力:除专属策略(pd、slo/slo-*)和显式单独选择load-balance之外,其余每个策略在后台都会静默混入load-balance的容量感知打分;当所选策略本身不按请求数路由时,还会额外混入least-request(以保持多端口 / 数据并行 Pod 路由在混合打分下仍能工作)。调用方对此无感知——ctx.Algorithm、响应头与Validate()仍然只反映用户实际请求的策略名。这使得任何单一策略都无法在负载不均衡门控之外,把流量持续引向已经过热的 Pod。
该机制的核心实现在appendLoadBalanceBlend(router.go):它对算法字符串做解析、附加load-balance:N(以及需要的least-request:N)后,走multiStrategyRouter的软打分(soft-scoring)管道。多策略路由的每个子策略先产出原始分,经 winsorize 裁剪(MAD 稳健化、剔除极端离群值)与 min-max 归一化后按权重系数加权求和,取最高分 Pod 为胜者(scoreAndRank)。
可用环境变量控制:
AIBRIX_ROUTING_AUTO_BLEND_LOAD_BALANCE_WEIGHT(默认1):混入的load-balance打分权重,设为0可整体关闭自动混合功能;AIBRIX_ROUTING_AUTO_BLEND_LEAST_REQUEST_WEIGHT(默认1):混入的least-request打分权重,设为0则从混合中剔除该项。
负载不均衡门控(Load-Imbalance Gate)
与自动混合互补的是中央负载不均衡门控:网关在selectTargetPod中、对最终生效的任意路由策略执行之前,统一调用ApplyLoadImbalanceGate(gateway.go),把候选 Pod 收窄到负载最低的子集。触发条件由两个环境变量控制:
AIBRIX_LOAD_BALANCE_IMBALANCE_FACTOR(默认2.0):3 个及以上 Pod 时,当max_req > factor × (mean_req + 1)触发门控;AIBRIX_LOAD_BALANCE_IMBALANCE_MIN_GAP(默认8):要求max_req − min_req的绝对差值至少为 8 才触发。
专属策略(pd、slo*)豁免此门控,因为它们自行管理各自的 Pod 子集。设计文档明确强调:该门控不是load-balance专属,而是对包括prefix-cache、prefix-cache-preble在内的所有非专属策略统一生效。详见 pkg/plugins/gateway/ENV_VARS.md。
如何扩展路由算法
路由框架是高度可插拔的,所有路由逻辑都通过Router接口表达(定义在 pkg/types/router.go):
// Router defines the interface for routing logic to select target pods. type Router interface { // Route selects a target pod from the provided list of pods. // The input pods is guaranteed to be non-empty and contain only routable pods. Route(ctx *RoutingContext, readyPodList PodList) (string, error) }参数说明:
ctx *RoutingContext:每请求的上下文,携带路由输入。关键字段(见 pkg/types/router_context.go):Algorithm—— 当前生效的路由策略名;Model—— 从请求体中提取的模型名;Message—— 原始 prompt 文本(可用于 token 级决策);User—— 可选用户标识(公平性类算法使用);ReqHeaders—— 入站 HTTP 请求头的副本;ConfigProfile—— 已解析的模型配置档案(策略覆盖、RPS 限制等),未设置时为nil;- 另有
BaseModel(LoRA 请求的基座模型名)、Stream(是否流式)、RequestID、ReqPath等字段可供算法读取。
readyPodList PodList:预过滤后的健康且可路由的 Pod 列表,保证非空。
返回值:选中 Pod 的IP:Port字符串(如"10.0.0.5:8080");选择失败时返回非 nil error。
新增一个算法(4 步):
- 在 pkg/plugins/gateway/algorithms 目录新建
*.go文件并实现Router接口; - 声明策略名的类型化常量:
const RouterMyStrategy types.RoutingAlgorithm = "my-strategy"- 在
init()中注册构造函数,使其在启动时被框架拾取:
func init() { Register(RouterMyStrategy, NewMyStrategyRouter) }- 通过
routing-strategy请求头按请求指定,或设置ROUTING_ALGORITHM=my-strategy作为全局默认。
RouterManager提供了Register/RegisterProvider/Validate/Select/Init/SetFallback等完整的管理原语(router.go),其中Validate负责校验策略名是否已注册,Select负责构造路由器实例。
可选能力接口(同样定义于 pkg/types/router.go):
QueueRouter—— 为内部维护队列、暴露队列长度的路由器提供支持(Len() int);FallbackRouter—— 通过委托给次级路由器实现链式路由,当主路由器无法决策时启用回退(SetFallback(RoutingAlgorithm, RouterProviderFunc));PodScorer—— 实现批量软打分的策略可进一步参与多策略混合路由(ScoreAll+Polarity),这是上文自动混合机制能够工作的前提。
路由策略的三种配置途径
方式一:每请求通过routing-strategy请求头
curl -v http://${ENDPOINT}/v1/chat/completions \ -H "routing-strategy: least-request" \ -H "Content-Type: application/json" \ -d '{ "model": "your-model-name", "messages": [{"role": "user", "content": "Say this is a test!"}], "temperature": 0.7 }'方式二:全局默认通过ROUTING_ALGORITHM环境变量
在 gateway plugin 部署中设置ROUTING_ALGORITHM=prefix-cache即可让所有未显式指定策略的请求默认走该策略。该环境变量在 pkg/plugins/gateway/types.go 与 util.go 中被读取。
方式三:通过模型配置档案(Config Profile)按模型指定
配置档案把路由设置直接嵌入模型 Pod 的注解,使发往该模型的每个请求自动获得正确配置,且可在注解内定义多个命名档案、用单个请求头在档案间切换。在Deployment、StormService或RayClusterFleet的 Pod 模板上添加model.aibrix.ai/config注解:
annotations: model.aibrix.ai/config: | { "defaultProfile": "default", "profiles": { "default": { "routingStrategy": "least-request", "requestsPerSecond": 200 }, "large-input": { "routingStrategy": "pd", "routingConfig": { "promptLenBucketMinLength": 8192, "promptTokensGte": 8192, "prefillScorePolicy": "prefix_cache", "decodeScorePolicy": "load_balancing" }, "requestsPerSecond": 50 }, "cache-friendly": { "routingStrategy": "least-kv-cache", "requestsPerSecond": 150 }, "offline-generation": { "routingStrategy": "throughput", "routingConfig": { "maxTokensGte": 2048 } } } }顶层字段:
| 字段 | 说明 |
|---|---|
lockedRoutingStrategy | 在模型级别锁定唯一路由策略。设置后优先级高于routing-strategy请求头、档案内routingStrategy与ROUTING_ALGORITHM环境变量;其余档案级旋钮(requestsPerSecond、routingConfig)仍按config-profile选中的档案生效 |
defaultProfile | 未发送config-profile请求头时使用的档案名,缺省回退到"default" |
profiles | 命名档案的映射,每个档案包含下述字段 |
档案字段:
| 字段 | 说明 |
|---|---|
routingStrategy | 该档案的路由算法(如least-latency、prefix-cache、pd) |
requestsPerSecond | 该档案的模型级 RPS 上限,超限请求以 HTTP 429 拒绝;省略或设为0表示不限 |
routingConfig | 算法专属设置的嵌套 JSON 对象。config-profile: auto时还从中读取请求级选择提示 |
请求时选择档案:
# 使用 "batch" 档案处理该请求 curl http://${ENDPOINT}/v1/chat/completions \ -H "config-profile: batch" \ -H "Content-Type: application/json" \ -d '{"model": "my-model", "messages": [{"role": "user", "content": "Summarize..."}]}'# 让网关依据 routingConfig 中的提示自动解析具体档案 curl http://${ENDPOINT}/v1/chat/completions \ -H "config-profile: auto" \ -H "Content-Type: application/json" \ -d '{"model": "my-model", "messages": [{"role": "user", "content": "Summarize..."}], "max_tokens": 4096}'config-profile: auto时,网关评估每个档案routingConfig中支持的请求级提示(当前支持promptTokensGte、promptTokensLt、maxTokensGte、maxTokensLt),仅当档案的全部提示都与请求匹配时才命中;若无档案匹配则使用defaultProfile(或"default")。promptTokens*使用网关已有的 prompt 文本提取与本地 token 估算,maxTokens*优先读max_tokens,缺失时读max_completion_tokens。
路由策略优先级(从高到低):
lockedRoutingStrategy(模型级锁定,始终最高,甚至压过routing-strategy请求头);routing-strategy请求头;- 解析后档案的
routingStrategy(由config-profile头、config-profile: auto的 routingConfig 提示或defaultProfile解析而来); ROUTING_ALGORITHM环境变量。
向后兼容:若 Pod 没有model.aibrix.ai/config注解,网关直接回退到routing-strategy请求头与ROUTING_ALGORITHM环境变量,存量部署无需迁移。配置档案的解析与校验实现在 pkg/plugins/gateway/configprofiles。
按工作负载特征选择策略
生产部署指南给出了按流量模式选型的建议(详见 docs/source/production/gateway.rst):
- 多轮对话 / prompt 前缀重叠高的负载 —— 用
prefix-cache:当请求共享公共前缀(系统提示词、few-shot 示例、对话历史)时,prefix-cache把请求路由到已在 GPU 内存中持有匹配 KV-cache 块的 Pod,减少冗余计算并改善延迟。算法内部细节与配置项见 prefix_cache_readme.md。 - 彼此独立、无前缀重叠的请求 —— 用
least-request:每个请求 prompt 唯一、无 KV-cache 复用收益时,least-request总是路由到在途请求最少的 Pod,实现均匀分布。 - 高吞吐且需要计算分离 —— 用
pd:prefill-decode 分离把 LLM 推理的两个阶段拆到专用 Pod 上,两者可独立定容与扩缩容,在高请求量下提升 GPU 利用率。部署要求与配置见 pd_readme.md。
需要强调的是:若请求未携带routing-strategy头,网关默认使用random路由。random适合测试与低流量场景,任何生产部署都应显式设置routing-strategy,避免依赖默认行为。
关键环境变量速查
路由相关核心环境变量汇总(完整清单见 pkg/plugins/gateway/ENV_VARS.md):
| 变量 | 默认值 | 说明 |
|---|---|---|
ROUTING_ALGORITHM | 无 | 默认路由算法(无每请求覆盖时生效) |
AIBRIX_ROUTING_AUTO_BLEND_LOAD_BALANCE_WEIGHT | 1 | 自动混合中load-balance打分权重;0关闭整个自动混合 |
AIBRIX_ROUTING_AUTO_BLEND_LEAST_REQUEST_WEIGHT | 1 | 自动混合中least-request打分权重;0从混合中剔除 |
AIBRIX_LOAD_BALANCE_IMBALANCE_FACTOR | 2.0 | 负载不均衡门控的倍数阈值(3+ Pod) |
AIBRIX_LOAD_BALANCE_IMBALANCE_MIN_GAP | 8 | 负载不均衡门控的最小绝对差值 |
AIBRIX_PREFIX_CACHE_TOKENIZER_TYPE | character | 前缀缓存哈希的 tokenizer 类型:character、tiktoken、remote |
AIBRIX_PREFIX_CACHE_STANDARD_DEVIATION_FACTOR | 1 | 前缀匹配 Pod 的负载标准差阈值因子 |
AIBRIX_PREFIX_CACHE_KV_EVENT_SYNC_ENABLED | false | 是否启用跨网关副本的 KV 事件同步(需配合 remote tokenizer) |
AIBRIX_PROMPT_LENGTH_BUCKETING | false | 是否按 prompt 长度分桶路由到 prefill Pod |
AIBRIX_PREFILL_SCORE_POLICY | prefix_cache | prefill Pod 选择策略:prefix_cache、least_request |
AIBRIX_DECODE_SCORE_POLICY | load_balancing | decode Pod 选择策略:load_balancing、least_request |
AIBRIX_STATESYNC_ENABLED | false | 启用 Redis 支撑的跨副本状态增量同步(多副本prefix-cache路由一致性的前提) |
PROMETHEUS_ENDPOINT | 空 | Prometheus HTTP API 地址;为空则跳过 PromQL 类指标查询 |
注意:依赖 PromQL 指标的策略(如least-latency、least-kv-cache等)需要正确配置PROMETHEUS_ENDPOINT以及可选的 Basic Auth 凭据(支持环境变量明文或 Kubernetes Secret 两种方式,后者优先)。
多副本部署注意事项
当 gateway plugin 副本数超过 1 时,需要满足两个条件才能保证路由决策一致(详见 docs/source/production/gateway.rst):
- 连接 Redis:gateway plugin 启动时读取
REDIS_HOST环境变量;若未设置或 Redis 不可达,每个副本只使用进程内状态,跨 Pod 路由将不一致; - 启用跨副本状态同步:设置
AIBRIX_STATESYNC_ENABLED=true激活 Redis 支撑的 delta-sync 机制,将 prefix-cache 等路由状态在副本间传播。该变量默认false,必须显式开启——漏配是扩展 gateway plugin 到多副本后prefix-cache路由不一致的最常见原因。
Redis 侧建议按并发请求量预留内存(大致每 1000 并发请求约 1 GiB),并通过redis_commands_duration_seconds指标监控其是否成为延迟瓶颈。
相关文档
- 网关路由功能指南:网关配置、请求头参考、速率限制、配置档案、Prometheus 接入与 OpenTelemetry 链路追踪;
- 生产网关部署指南:资源与副本数、Redis 多副本状态同步、buffer/连接/QPS 策略旋钮;
- prefix-cache 算法细节:前缀缓存路由的内部机制与配置;
- PD 分离算法细节:prefill-decode 分离的部署要求与配置;
- 网关插件环境变量清单:全部网关环境变量的类型、默认值与源码出处。
【免费下载链接】aibrixCost-efficient and pluggable Infrastructure components for GenAI inference项目地址: https://gitcode.com/GitHub_Trending/ai/aibrix
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考