
说实话我一开始对“LLM API Gateway”这个项目是有点不以为然的。通用API网关那么多Kong、APISIX、Spring Cloud Gateway哪个不成熟为什么大模型应用还要单独折腾一个网关直到我们自己的AI应用从Demo走到线上被真实流量教育了一轮之后我才彻底想明白LLM场景里的流量治理跟普通HTTP接口完全是两个世界。这篇文章是根据我们团队一份真实开发计划整理出来的标题后面那句“AI敏捷版·已按实际代码修正”不是装点门面而是因为原计划确实是先用AI辅助梳理的框架后来在编码、压测、上线的过程中我们不停地拿实际遇到的问题回头改计划改了好几轮。所以这篇文章不是PPT式的规划而是把踩过的坑、验证过的方案、以及生产环境真正需要的增强点一次性讲清楚。适合正在做AI应用后端、大模型服务治理或者准备把LLM能力产品化的团队参考。1. 项目定位为什么LLM项目要单独做一个API网关1.1 通用API网关在大模型场景下的“水土不服”先说说我为什么觉得通用网关不够用。我们团队最早确实打算直接用现成的API网关把请求转发到各个大模型供应商的接口就完事了。结果一接入就发现LLM调用的特征跟普通REST API差别太大了第一响应是流式的。大模型生成内容是一个字一个字往外蹦的走的是SSEServer-Sent Events长连接。通用网关对短请求短响应的优化逻辑在这里完全不适用如果哪一层做了缓冲、做了Gzip压缩第一个token能给你卡出好几秒延迟用户体感就是“这AI是不是死了”。第二上游耗时极长。普通API读超时设置为2秒、5秒很正常但大模型接口从发起请求到完整返回几十秒甚至几分钟都很常见。如果网关还按传统超时策略来你自己的服务没超时网关先把连接掐了用户体验直接崩。第三成本敏感且计量复杂。普通API按调用次数算钱大模型按Token算钱。一次流式请求可能产生几万Token不同模型单价不同不同部门、不同应用消耗不同。这笔账如果不在网关层集中算清楚月底对账能把你逼疯。第四模型供应商多且协议不统一。有的厂商兼容OpenAI格式有的厂商有自己的原生协议同一家的不同模型参数也千差万别。客户端要是直连这些接口一旦切换供应商整个应用层代码都要跟着改。这些问题叠加在一起结论就很清晰了我们需要一个懂LLM语义、能处理流式、能算Token、能管理多渠道的专用网关而不是一个通用的流量转发器。1.2 生产环境网关要扛住的核心任务这个LLM API Gateway项目本质上要解决四个层面的问题流量入口统一。所有内部应用的AI请求一律走网关进出客户端不直接接触任何模型供应商的地址和密钥。这样密钥管理和安全审计就收敛到一个点上了。成本与配额治理。按应用、按部门设置不同的调用额度和速率限制超了就拒绝或者走降级。同时把每次调用的模型、Token消耗、费用预估全部记录成审计日志。模型适配与高可用。网关内部适配多家供应商日常按权重分发流量某家出现故障或者限流自动摘除切到备用渠道单次调用失败还能按策略重试。可观测性。每个请求从进入网关到拿到完整响应的全链路都能看到延迟、Token数、错误码、模型名称。这些指标既要支撑实时监控告警也要支撑后续的成本分析和容量规划。2. 整体架构与关键技术选型2.1 分层设计从接入到适配的四层结构我们的网关架构没有搞什么花活就是清晰的四层接入层负责处理客户端连接、鉴权、协议解析。HTTP/HTTPS的请求在这里被接收SSL终止在这一层完成。客户端拿到的永远是网关的域名而不是某个模型供应商的地址。路由治理层这是网关的大脑。根据请求中的模型名、应用标识、请求特征决定转发到哪个供应商、哪个模型。同时在这一层执行限流、配额检查、密钥分发、超时控制、重试策略。模型适配层针对不同供应商实现各自的Adapter把上游五花八门的接口规范化成我们内部的统一协议。上层治理逻辑根本不用关心对方是OpenAI协议还是自研协议。存储观测层依赖Redis做分布式限流和缓存依赖日志系统收集审计数据依赖PrometheusGrafana做指标监控。这个分层思路的核心好处是每一层的职责单一替换成本低。比如以后想新增一家模型供应商只需要实现一个新的Adapter路由规则加一条配置其他层完全不动。2.2 为什么选Redis 7做限流和配置管理限流这种事单机内存就能做为什么非要引入Redis因为生产环境网关必然是多副本部署的单机的限流计数器在集群模式下就是摆设。必须有一个所有节点共享的存储才能做到全局限流。选Redis 7而不是老版本有几个很实际的理由Redis 7对ACLAccess Control List的支持更加成熟稳定。我们可以为网关服务单独创建一个最小权限用户只允许访问限流相关的key前缀就算网关被拖库或者代码有漏洞攻击者也拿不到Redis的全部数据更删不了别人的key。Redis 7在性能上做了不少优化在同样资源配置下吞吐更高、延迟更低。做限流这种高频操作Redis每个命令省零点几毫秒在高流量下累积的效果很明显。另外我们还要用Redis做配置的轻量缓存。比如模型供应商的健康状态、路由权重这类变更频率不高的数据放在Redis里比每次查数据库要快得多也减少下游依赖的压力。2.3 Docker Compose生产部署Redis不只是起个容器那么简单很多人觉得生产环境用Docker Compose部署Redis是很初级的事其实恰恰是这种“看似简单”的地方最容易埋坑。我们最终的docker-compose.yml关键片段长这样services: redis: image: redis:7-alpine container_name: llm-gateway-redis restart: always ports: - 127.0.0.1:6379:6379 command: redis-server --appendonly yes --appendfsync everysec --maxmemory 2gb --maxmemory-policy allkeys-lru --requirepass ${REDIS_PASSWORD} volumes: - redis-data:/data - ./redis.conf:/usr/local/etc/redis/redis.conf sysctls: - net.core.somaxconn1024 healthcheck: test: [CMD, redis-cli, -a, ${REDIS_PASSWORD}, ping] interval: 10s timeout: 3s retries: 5 deploy: resources: limits: memory: 4g logging: driver: json-file options: max-size: 50m max-file: 3每个配置都有它存在的理由appendonly yes加appendfsync everysec开启AOF持久化保证网关节点重启后限流计数不至于全丢。为什么不用RDBRDB快照粒度过粗Redis如果宕机最多丢近一段时间的数据对于限流场景可能造成一瞬间的限额失控。maxmemory 2gb配合maxmemory-policy allkeys-lru给Redis设一个内存天花板防止缓存数据无限膨胀把机器内存吃光。LRU策略在网关缓存场景下是合理选择。只绑定127.0.0.1Redis内网专供网关使用不对公网开放。生产环境这个习惯必须养成。资源限制和日志轮转防止Redis异常时拖垮整个宿主机也防止日志文件把磁盘打满。healthcheck让Compose编排和上层调度能感知Redis的存活状态避免网关带着故障Redis硬跑。提示Redis密码不要写在compose文件里用环境变量注入。如果严格要求安全合规建议在Redis中单独开启ACL用户管理而不是只依赖requirepass。3. 核心功能实现细节3.1 多Provider适配与动态路由前面说过模型适配层的核心是定义统一接口然后分供应商实现。我们不直接对接各家的SDK而是自己包一层HTTP调用原因很简单SDK的版本更新不可控而且很多SDK对流式处理的支持不够灵活。内部抽象出来的核心接口大概长这样public interface LlmProviderAdapter { String providerName(); CompletableFutureLlmResponse chatAsync(LlmRequest request); FluxLlmChunk chatStream(LlmRequest request); boolean healthCheck(); ProviderStatus status(); }每个供应商实现一个Adapter比如OpenAICompatibleAdapter、AnthropicAdapter、BaiduAdapter。统一成这个接口之后上层的路由逻辑就非常干净了public class LlmRoutingService { public FluxLlmChunk route(String modelName, LlmRequest request) { RouteRule rule routeRules.get(modelName); LlmProviderAdapter adapter adapterRegistry.get(rule.targetProvider()); return adapter.chatStream(request); } }路由规则我们放在配置中心运行中热更新。比如某家供应商最近频繁限流运维可以直接调整该供应商的权重网关会自动把流量切向备用渠道业务方根本无感知。3.2 基于Redis Lua的分布式限流、配额与ACL配置限流是整个网关里技术含量最高的一块也是生产环境最容易出问题的地方。我们最终采用令牌桶算法通过一段Lua脚本在Redis里原子执行-- KEYS[1]: 限流key如 llm:limit:app001:chat -- ARGV[1]: 桶容量 capacity -- ARGV[2]: 每秒恢复速率 rate -- ARGV[3]: 本次请求消耗的令牌数 cost -- ARGV[4]: 当前时间戳毫秒 local capacity tonumber(ARGV[1]) local rate tonumber(ARGV[2]) local cost tonumber(ARGV[3]) local now tonumber(ARGV[4]) local tokens tonumber(redis.call(GET, KEYS[1]) or capacity) local lastRefill tonumber(redis.call(GET, KEYS[1] .. :ts) or now) if tokens 0 then tokens 0 end local elapsed now - lastRefill if elapsed 0 then elapsed 0 end tokens math.min(capacity, tokens elapsed * rate / 1000) redis.call(SET, KEYS[1], tokens) redis.call(SET, KEYS[1] .. :ts, now) if tokens cost then redis.call(SET, KEYS[1], tokens - cost) return 1 end return 0为什么用Lua而不是用Redis事务或者多次GET/SET三个原因一是Lua脚本在Redis里是原子执行的不会出现并发下多拿令牌的问题二是脚本只在Redis服务端执行一次网关端不用写复杂的加锁逻辑性能也好三是限流逻辑改起来只动脚本不用重新发布网关代码。配额和限流是两回事。限流管的是“每秒/每分钟最多多少个请求”配额管的是“这个月总共能消耗多少Token”。配额的计算我们单独用一组key存储每次调用结束后从响应里取出Token消耗量累加进去。配额维度可以控制到应用、部门、甚至单个用户。Redis的ACL配置也要用起来。给网关服务分配一个专用用户只允许操作网关自己的key空间ACL SETUSER llm-gateway on ${REDIS_GATEWAY_PASSWORD} ~llm:* read write -admin这样即使Redis被扫描到其他业务数据也是隔离的权限边界清晰可控。3.3 流式响应SSE转发与超时管理流式转发是整个网关最脆弱、最容易出问题的环节。我们的实践是采用响应式编程模型来处理流网关收到上游SSE数据块后不缓存、不聚合直接推给客户端。处理SSE必须注意几个细节第一禁用缓冲。网关到客户端这一段必须显式关闭响应缓冲那行经典的响应头X-Accel-Buffering: no在Nginx后面做反代时必须设置。同时关闭不必要的Gzip或者至少保证SSE流不被Gzip层积压。第二首token超时和整体超时分开设置。大模型接口可能等了30秒才吐出第一个token但一旦开始吐就很流畅。所以我们把超时拆成“连接超时”“首token超时”“总耗时上限”三个维度超时类型适用场景建议值连接超时TCP建立5秒首token超时Chat类大模型60秒首token超时Embedding类小模型10秒总耗时上限长文本生成10分钟这个表格里的值是根据线上实际压测调出来的。注意连接超时设太短没用模型冷启动、排队都可能造成连接建立后长时间无响应首token超时才是LLM场景的核心指标。第三客户端断开要传播到上游。用户可能中途关闭页面这时代码里必须调用取消操作把上游的请求也掐掉。否则网关这边连接断了上游还在吭哧吭哧生成内容Token照扣钱照算成本就白白流失了。流式框架的onCancel回调一定要处理干净。3.4 重试策略不能无脑重试重试是所有网关都会做的功能但在LLM场景里坑特别多。我们踩过最大的坑是某个供应商接口偶发500网关自动重试一次结果两次都成功了用户收到两条完整的回复账单上扣了双倍的钱。之后我们定下几条铁律流式请求一律不自动重试。因为部分供应商是Push模型服务端已经持续推送内容你无法判断它究竟是失败还是只是慢。要重试也只能让业务方显式传入请求幂等键由网关去重。非流式请求只重试幂等场景。比如Embedding向量生成这类请求重试相对安全。但也必须限制重试次数最多2次超过就返回错误。重试要退避不能立即重发。三次重试间隔建议是1秒-2秒-4秒加上一定的随机抖动不然多副本网关同时重试容易把上游打爆。4. 敏捷迭代与AI辅助开发的实战方式4.1 为什么这类项目要跑敏捷而不是重流程有阵子团队里有人讨论“敏捷和CMMI的关系”问我们这种网关项目要不要引入严格的CMMI级别管理。我的观点很直接做AI基础设施类项目敏捷是必须的CMMI式的重流程会拖死你。原因在于LLM生态的变化速度太快了。以模型供应商的接口为例半年内可能出两三次重大版本调整比如引入新的参数、改变流式数据格式、增加新端点。如果你把需求冻结按阶段推进等你的设计方案走完评审供应商的协议早就变了。敏捷的短迭代、快速验证、拥抱变化天然适合这种场景。当然这不等于不要工程严谨性。我们的做法是流程上走敏捷技术上的核心规范比如代码评审、压测通过才能合入、灰度发布一条不少。敏捷管的是节奏不是纪律的替代品。4.2 如何把一个开发计划拆成可运行版本原计划里我们把整个项目拆成四个里程碑每个里程碑结束都有一个“能见人的东西”M1 打通链路客户端发出一个请求网关成功转发到某一家供应商流式返回正常。这一个目标看似简单实际上要完成接入层、路由层、适配层的最小闭合。M2 治理能力限流、配额、密钥管理、超时控制、审计日志全部上线。到了这个阶段网关才真正具备“可控”的能力可以接入内部应用试跑。M3 高可用与观测多副本部署、故障自动切换、Prometheus指标、调用链追踪、告警规则全部就位。M4 成本治理与精细化运营按部门、按项目的成本报表模型性价比分析跨供应商的最优路由推荐。每个里程碑都控制在两周左右结束前必须做一次内部演示和复盘。这样项目始终处于“可用状态”而不是憋一个大版本最后崩盘。4.3 AI辅助开发的真实正确用法标题里那个“AI敏捷版”不是白写的。我们在整个开发过程中确实大量使用了AI辅助但我对AI辅助的态度一直很清醒AI生成的东西是草案不是答案。我们总结出来一套还算好用的流程用AI做计划初稿把项目目标、技术约束、已知依赖扔给AI让它生成任务分解、时间线、风险点。这一步能节省大量从零梳理的时间而且AI往往能补出一些容易遗漏的细节。AI生成的计划必须和实际代码对齐写代码过程中一发现计划不合理立刻回改计划。我们的计划文档大标题下都有“修正记录”章节专门记录哪些地方和初稿不一样。这才是“已按实际代码修正”的真正含义。AI写单元测试很靠谱网关这类服务接口边界清晰非常适合让AI生成参数化测试和异常分支测试。AI特别擅长从接口定义推出一堆边界情况比人肉手写测试覆盖率高得多。AI解释复杂机制比如Redis Lua脚本的原子性细节、流式框架的背压机制让AI结合源码讲一遍比自己啃源码快很多。但关键结论必须自己验证不能直接抄进代码。注意AI辅助生成限流、鉴权这类安全敏感代码时务必人工Review。这类代码出错就是生产事故级别的AI可不会替你背锅。5. 生产环境调试与问题排查实录5.1 用Arthas定位线上网关问题真实案例网关是Java技术栈生产环境排查神器Arthas我们基本天天用。很多人问Arthas能不能用在生产环境我的答案是只要做好权限控制和审计Arthas是生产环境排查问题不可或缺的工具。举一个我们真实遇到过的案例。某次线上流量激增网关的P99延迟从800ms飙到5秒监控面板上一片飘红。常规手段看日志、看链路追踪半天没定位到瓶颈。我们用Arthas做了三件事第一dashboard看全局。线程数、内存、GC情况一目了然很快发现Old区占用很高且GC耗时明显增加。第二thread -n 3看最忙的线程。揪出了CPU占用最高的几个线程线程栈直接指向了我们Redis连接池的获取逻辑。第三trace某个具体的限流方法看每个子调用的耗时分布。结果发现Redis的响应本身很快但应用侧获取连接时频繁等待。由此定位到根因Redis连接池配置过小且连接泄漏问题早就有隐患流量一大就彻底暴露了。修复后P99降到1秒以内。Arthas的使用有一条纪律生产环境上机前必须经过审批操作过程全程录屏使用完毕后退出attach避免留下后门。再强大的工具也要有使用边界。5.2 线上高频问题流式连接堆积、线程耗尽、超时误判总结了几个高频问题都是真实线上踩过的新手团队特别容易中招。问题一流式连接堆积导致线程耗尽。网关采用响应式模型之后线程数是少了但如果客户端不发关闭信号上游连接却异常断掉连接还是会泄漏。必须给每个连接设置空闲超时同时监控活跃连接数设置上限。问题二超时误判把正常请求掐断。早期我们的首token超时定的是10秒结果模型排队严重时超过10秒没出token就把请求取消实际如果多等几秒就能成功。后来我们把首token超时放宽到60秒错误率直接下降了一个数量级。超时参数一定要基于线上数据统计来定不能拍脑袋。问题三日志风暴。限流拒绝的请求如果每个都打印一条ERROR日志量一大直接把日志系统打爆。后来我们统一把限流拒绝打为WARN级别并按1分钟维度聚合统计既不影响排障也不会冲垮日志管道。5.3 日志、监控与链路追踪的三件套网关的运维观测三件事缺一不可日志要结构化。每一次请求统一输出一条JSON日志字段包括请求ID、应用标识、模型名、Provider、Token消耗、耗时、错误码、限流命中等。这是后续做成本分析、问题追溯的基础。监控指标要分层。RED指标Rate错误率、Errors错误数、Duration延迟是基本盘LLM网关额外需要关注Token吞吐量、首Token延迟、Token生成速率、按模型和应用的消耗分布。这些指标配合PrometheusGrafana做成看板值班人员一眼能看出问题。链路追踪要贯穿全链路。从客户端进网关到网关转发上游再到上游返回流式数据每个环节都要有TraceSpan。我们用的方案是在网关层生成traceId随HTTP Header透传给上游供应商供应商侧虽然不一定配合打完整链路但至少自己内部能拧成一根线去查。6. 常见坑点速查与上线前检查6.1 我踩过的几个有代表性的坑这里把踩过的坑整理成一张速查表给后来的人直接避雷坑点现象根因解决方案客户端密钥直接写在代码里一次前端代码泄露所有模型Key全暴露想省事没走网关鉴权强制所有流量走网关应用侧只用网关发放的短期凭证流式数据被Gzip缓冲用户侧迟迟刷不出文字反代/网关默认开了压缩对SSE路径禁用缓冲压缩测试环境专门验证流式体验Redis连接池过小流量高峰期获取连接超时默认配置没有按并发评估压测得出合理连接池大小运维设告警重试导致重复计费用户抱怨被扣了两次钱流式请求自动重试流式一律不自动重试提供幂等键由业务方控制热点Key压垮Redis某个爆款应用限流key访问量巨高单一key分片不均对热点应用按用户ID做哈希分片拆成多个key这些坑单看都觉得“我不会犯”但真实开发节奏一快全都会犯。建议每个坑都写进团队的故障复盘文档单人踩坑、全员长经验。6.2 上线前必须做的几项检查网关不像普通业务服务它一旦挂了所有下游AI应用全部瘫痪。上线前一定要过一遍这个检查清单压测通过不能只压正常流量要压“限流触发”和“上游故障”两个场景确认降级行为符合预期。幂等确认梳理所有可以重试的接口确认上游是否支持幂等键不支持的一律不加自动重试。优雅关闭网关多副本滚动发布时旧节点必须等正在处理的流式请求结束才下线不能直接杀进程。日志脱敏请求和响应中可能包含用户Prompt和模型输出日志采集前要做脱敏处理敏感信息绝对不能进日志。成本告警配置Token消耗增长率的告警日消耗突增要能第一时间发现防止异常流量产生天价账单。每一项检查都有真实的事故案例作为背景不是走形式。上线前多花半天过一遍能省掉后面无数个不眠夜。这套LLM API Gateway从设计到落地最深的体会是网关的好与坏不是架构图画得有多漂亮而是线上流量的每一次异常你都能快速定位、从容处理。AI辅助确实把我们从繁琐的重复劳动里解放出来了但最终判断还是要靠人——尤其是那些藏在超时参数、连接池大小、重试策略背后的权衡工具给不了你答案只能靠你在真实流量里一遍遍打磨再回头修正那份计划书。计划不需要完美计划能跟得上现实的变化并且团队的每个人都清楚哪里改了、为什么改这才是“敏捷”两个字真正的分量。