LLM 服务网关设计:流量分发、鉴权、限流、多模型路由、负载均衡、接口版本管理(C++ 网关落地)
前言前面完成了多模态图文 RAG 的 C 接口封装我们已经能在后端服务里调用 LLM、Embedding、CLIP 多模态模型。当项目从小 demo 走向生产环境当存在多个模型实例、多个业务方同时调用模型服务的时候不能让业务客户端直接连接底层推理服务。底层推理服务如 llama.cpp、vLLM、text-generation-webui的核心职责只有一件GPU/CPU 上跑模型推理。鉴权、计费、限流、路由、熔断、日志审计、请求重试、流式转发这些通用流量治理逻辑如果全部塞进推理服务内部会极大增加模型服务代码复杂度还会引入很多不稳定因素。LLM 服务网关就是 AI 集群的统一流量入口。本质是面向大模型场景的专用 API 网关承接所有客户端请求做前置校验和流量调度再转发到后端各个模型实例同时把模型返回结果尤其是 SSE 流式输出透传给客户端。普通 Web 网关 Nginx、OpenResty 可以做基础反向代理但原生缺少 LLM 场景专属能力Token 计量、长任务并发控制、FunctionCall 透传、模型灰度路由、额度扣减。自研 C 网关就可以补齐这些定制化能力也是面试里非常加分的项目亮点。本篇会从业务痛点、整体架构、模块拆解、算法选型、完整可扩展 C 代码、性能压测指标、线上故障案例、面试深挖问题完整展开。一、LLM 网关业务痛点与架构总览1.1 直连模型服务的一系列工程问题多实例多模型管理混乱同时部署 7B、13B、Embedding、CLIP 多模态模型每个模型多实例扩容客户端需要维护大量后端地址模型实例上下线需要业务方改配置无法无感扩容缩容。算力资源无法管控任何人拿到接口地址就可以无限调用长文本 prompt 大量消耗 GPU 显存没有配额、鉴权算力资源容易被耗尽。缺少熔断保护故障雪崩某一个推理节点 OOM、GPU 显存溢出报错请求继续往故障节点转发大量请求超时堆积拖垮整个业务。无法做灰度与版本管理模型迭代升级新旧版本模型需要并行验证直连方式很难按比例切流做灰度测试。流式 SSE 转发困难LLM 主流采用 SSE 长流式输出普通反向代理容易出现超时截断、chunk 粘包同时很难实时统计流式输出的 token 消耗。缺少全链路观测调用耗时、输入输出 token、错误码、用户调用量无法统一采集无法做计费、告警、故障排查。1.2 LLM 网关分层架构整体分为四层接入层异步网络 IO 服务接收 HTTP/HTTPS 请求解析请求头、请求体参数合法性校验限制请求体最大长度过滤非法请求。安全与计量层API Key 鉴权、用户身份解析、用户 Token 额度校验、预扣额度、调用日志落库、全链路 TraceID 注入。流量调度层模型路由、灰度权重分发、后端节点负载均衡、健康检查、限流排队、熔断降级、请求优先级调度。代理转发层异步请求转发、SSE 流式 chunk 透传、后端结果解析、token 统计、失败自动重试、错误封装返回。后端集群LLM 推理实例、Embedding 服务、多模态 CLIP 服务全部对客户端透明只和网关通信。1.3 网关存储依赖Redis存储 API Key 信息、用户剩余 token 配额、后端节点健康状态、限流计数器MySQL持久化调用记录、账单、用户信息日志系统全链路日志、访问日志、异常日志。二、网关核心模块深度原理2.1 API Key 鉴权 Token 配额管控鉴权流程客户端请求 Header 携带Authorization: Bearer xxx-api-key。网关收到请求提取 ApiKey查询 Redis 缓存Key 是否存在、是否启用、所属用户 ID。缓存失效时回源 MySQL 查询。非法 key 直接返回 401 拒绝访问。配额管理LLM 场景核心传统接口按请求次数计费大模型场景算力消耗由输入 token 输出 token 总量决定必须按 token 做额度管控。 流程分为预扣 结算两步请求到达网关解析 prompt调用 token 计算器预估输入 token 数量预扣用户额度额度不足直接返回 429拒绝调用模型推理完成流式输出需要等待流结束拿到模型返回真实输入 输出 token做额度修正如果客户端中途断开流式连接模型中断生成要做额度回滚避免扣取未生成 token 的费用。坑点token 预估存在误差预估大于实际消耗需要异步补偿返还差额预估偏小可能出现超额度调用。工程上预留一定的缓冲额度。2.2 多模型路由与灰度发布请求体中携带model字段网关路由表匹配模型名称转发到对应的后端实例集群。 路由支持三种策略固定路由模型名称绑定固定后端集群例如llm-7b路由到 7B 推理集群embedding-bge路由向量模型版本路由model 字段携带版本llm-7b-v1、llm-7b-v2新旧模型集群隔离权重灰度路由同一个模型名称配置新旧两组节点按流量百分比分配。例如 90% 流量到老版本10% 切到新版本持续观测错误率、token 生成速度无异常再逐步放大流量。灰度观测指标请求错误率、首 token 耗时 TTFT、单 token 生成速度、GPU 显存使用率。一旦指标恶化网关自动切回全量老版本无需人工改配置。2.3 负载均衡 后端节点健康检查同一个模型集群会部署多个推理实例横向扩容分担压力。 负载均衡可选策略轮询简单平均分发适合请求算力消耗差距不大场景最小连接数优先把请求分配给当前并发任务更少的后端节点LLM 长推理任务首选策略。主动健康探测网关后台起定时器周期性向后端推理节点发送健康探测请求/health 接口。探测不仅检查端口连通性还要验证模型是否加载完成、GPU 是否正常。连续 N 次探测失败节点标记为不健康路由时直接跳过不再分发流量恢复后连续多次探测成功重新加入可用节点列表。只探测端口是典型线上大坑端口能通不代表模型加载完成GPU 可能 OOM 卡死但是端口依旧存活。2.4 LLM 专属限流、排队、熔断降级普通 Web 接口用 QPS 限流但是 LLM 推理是长耗时任务单次请求可能持续几秒甚至几十秒QPS 参考意义很小。LLM 网关限流核心指标后端并发推理任务数控制同一时间在 GPU 上运行的请求总数保护显存不被打满。请求排队队列并发达到上限新请求进入内存等待队列配置队列最大长度队列满直接返回 429每个请求设置排队超时超时直接失败避免请求无限堆积。请求优先级支持业务优先级高优先级业务请求优先出队抢占排队位置保障核心业务。熔断机制统计每个后端节点错误率单位窗口内错误比例超过阈值触发熔断冷却窗口期内不再转发流量等待节点恢复。降级策略集群过载时可以自动降级切换到轻量化备用模型、或者直接返回预设提示信息保护核心业务不发生雪崩。2.5 SSE 流式透传LLM 网关最特殊模块普通 HTTP 代理是等待后端完整接收 response再返回客户端。LLM 流式 SSE 不可以这样做。 模型服务会持续返回data: {...}的 chunk 片段网关需要收到一段立刻转发一段一边透传 chunk一边实时累加统计输出 token。难点长连接保活设置心跳包防止中间网络代理断开长连接客户端中途断开连接网关需要立刻通知后端模型中断推理释放 GPU 资源避免无效占用显存chunk 存在粘包、分包网关需要正确分割 SSE 数据流不能把多条消息合并。三、C 高性能网关完整核心代码基于 Boost.Asio 异步 IO说明下面是可扩展工程骨架生产环境可基于此继续完善鉴权、Redis 交互、健康检查定时器、埋点日志。#include iostream #include string #include vector #include map #include mutex #include queue #include boost/asio.hpp #include boost/asio/ssl.hpp using namespace boost::asio; using ip::tcp; // 后端模型节点元信息 struct ModelNode { std::string host; int port; bool healthy; int current_conn; // 当前并发任务数最小连接数负载均衡使用 std::mutex mtx; }; // 模型路由表模型名称 - 后端节点列表 std::mapstd::string, std::vectorModelNode model_route_table; std::mutex route_mtx; // 全局排队任务队列简单实现请求排队 struct Task { std::string model_name; std::string request_body; }; std::queueTask task_queue; std::mutex queue_mtx; const int MAX_QUEUE_SIZE 200; // 最小连接数负载均衡选择健康、当前并发最少的节点 ModelNode* selectMinConnNode(const std::string model_name) { std::lock_guardstd::mutex lk(route_mtx); auto it model_route_table.find(model_name); if (it model_route_table.end()) return nullptr; auto node_list it-second; ModelNode* target nullptr; int min_conn INT_MAX; for(auto node : node_list) { if(node.healthy node.current_conn min_conn) { min_conn node.current_conn; target node; } } return target; } // 异步转发SSE流式请求透传chunk统计token void forwardSSE(io_context io, const std::string model, const std::string body) { ModelNode* node selectMinConnNode(model); if(node nullptr) { std::cout [Gateway] 无可用健康后端节点, model: model std::endl; return; } // 增加节点并发计数 { std::lock_guardstd::mutex lk(node-mtx); node-current_conn ; } std::cout [Gateway] 转发请求到 node-host : node-port std::endl; // 异步http连接SSE chunk流式透传逻辑 // 收到后端返回data chunk直接转发给客户端解析chunk内token增量累加统计 // 连接结束/断开时减少节点并发计数 // node-current_conn --; } // 任务消费协程消费排队队列 void taskWorker(io_context io) { while(true) { Task task; { std::lock_guardstd::mutex lk(queue_mtx); if(!task_queue.empty()) { task task_queue.front(); task_queue.pop(); } else { break; } } forwardSSE(io, task.model_name, task.request_body); } } // 请求入队接口 bool enqueueRequest(const std::string model, const std::string body) { std::lock_guardstd::mutex lk(queue_mtx); if(task_queue.size() MAX_QUEUE_SIZE) { return false; //队列已满返回429 } task_queue.push({model, body}); return true; } int main() { io_context io; // 初始化路由表部署两组llm7b实例、一组embedding实例 { std::lock_guardstd::mutex lk(route_mtx); model_route_table[llm-7b] { {127.0.0.1,8081, true, 0}, {127.0.0.1,8082, true, 0} }; model_route_table[embedding-bge] {{127.0.0.1,8090, true,0}}; } std::cout C LLM Gateway Started, listen on 8000 std::endl; // 启动http监听、任务消费循环 return 0; }代码扩展方向接入 hiredis读取 API Key、用户额度增加定时器周期性执行后端健康探测增加全链路 TraceID日志携带 trace_id增加 token 预估、预扣额度逻辑SSE 分包解析实时解析返回的 token 数量请求超时定时器清理长时间未返回的请求。四、线上工程踩坑全集SSE 长连接超时LLM 生成慢默认 HTTP 代理超时断开。网关需要单独配置长读写超时并且定时发送心跳注释包维持连接。并发计数竞态多线程并发修改节点 current_conn不加锁会导致并发统计错乱限流失效GPU 过载。token 统计不一致流式输出客户端中途断开模型停止生成如果网关没有回滚预扣额度会造成用户多扣费。健康检查只检测端口GPU OOM进程存活但推理卡死端口正常网关依旧分发请求大量请求全部超时。健康探测要调用模型真实推理接口。请求体无上限超长 prompt几 MB 的请求包不做限制会造成网关内存持续暴涨内存溢出。缺少全链路 Trace网关、模型服务日志没有统一 trace_id出现超时、报错无法追踪请求流转排查困难。排队队列无限堆积没有最大队列长度和排队超时流量洪峰时请求全部堆积所有请求大面积超时。五、可观测指标网关监控大盘必备网关 QPS、成功 / 失败请求数TTFT首 token 生成耗时、总推理耗时输入 token 总量、输出 token 总量排队等待时长、队列积压请求数量每个后端实例并发连接数、错误率401 鉴权失败、429 限流拒绝请求数量熔断触发次数、降级触发次数。六、面试高频深挖问答Q1自研 C LLM 网关相比 Nginx 反向代理优势在哪ANginx 适合通用 HTTP 流量转发对 LLM 场景定制能力弱。自研 C 网关可以深度集成 token 预估、额度扣减、按并发任务限流、SSE 的 token 实时统计、模型灰度权重、请求优先级调度。私有化项目中可以减少中间组件依赖全链路可控方便接入业务计费逻辑。缺点是需要自行开发维护开发成本更高。Q2什么是 TTFT网关层面可以做哪些优化降低 TTFTATTFT 是首 token 输出耗时代表用户看到第一个文字的等待时间。网关侧优化减少排队等待、健康节点精准路由、请求优先级调度把高优先级请求优先调度减少网关内部拷贝开销异步 IO 降低转发延迟。TTFT 瓶颈大多在 GPU 推理侧。Q3用户客户端断开 SSE 连接网关要做什么操作A网关立刻向后端推理服务发送中断请求终止本次生成释放 GPU 显存回滚用户预扣的 token 额度日志记录用户主动断开事件上报监控。如果不中断GPU 会继续生成无用 token持续占用显存。Q4熔断和限流的区别A限流是预防防止大量请求打满后端资源在流量进入时做拦截熔断是故障容错后端节点已经大量报错网关主动停止向故障节点转发流量等节点恢复属于故障发生后的保护手段。Q5网关做 token 预扣如果预估 token 大于实际消耗怎么处理A推理结束拿到真实 token 消耗计算差额异步返还多余额度到用户账户后台任务补偿同时日志记录差额用于后续优化 token 预估模型准确度。七、总结LLM 网关是 AI 服务生产化的核心组件核心能力不是简单反向代理而是面向大模型长推理任务的流量治理、权限计量、多实例调度。LLM 场景不能照搬传统 Web 的 QPS 限流优先控制后端并发推理任务搭配请求排队、熔断降级保护 GPU 算力。SSE 流式透传是 LLM 网关核心难点不仅要转发数据流还要处理连接中断、token 统计、资源释放。C 异步 IO 适合自研私有化轻量网关可深度定制业务逻辑是简历上体现架构落地能力的亮点。网关层必须配套完善监控指标TTFT、token 消耗、队列积压、错误率是线上运维最核心观测指标。