1.3 常见架构反模式:传统 Web 套路在大模型服务上怎么翻车 前两节立了四层抽象和指标标尺,这一节反过来看——把我亲身经历或近距离观察过的三个真实失败案例摆出来,剖析经典 Web 的"正确经验"为什么在大模型对话里变成了"致命陷阱"。失败的镜像往往比成功的说教更刻骨,而且这三个案例有个惊人的共性:它们都不是技术选型不够新,而是"分层错位"——把某一层该做的事甩给了另一层,或者用错了口径。这个共性正是本书坚持"先把分层和口径讲透"的原因。 反模式一:无状态水平扩容,把推理集群当"普通微服务" 这是从经典 Web 后端转来做大模型对话的团队,最容易踩的第一个坑。
前两节立了四层抽象和指标标尺,这一节反过来看——把我亲身经历或近距离观察过的三个真实失败案例摆出来,剖析经典 Web 的"正确经验"为什么在大模型对话里变成了"致命陷阱"。失败的镜像往往比成功的说教更刻骨,而且这三个案例有个惊人的共性:它们都不是技术选型不够新,而是"分层错位"——把某一层该做的事甩给了另一层,或者用错了口径。这个共性正是本书坚持"先把分层和口径讲透"的原因。
这是从经典 Web 后端转来做大模型对话的团队,最容易踩的第一个坑。
场景:某团队理所当然地把推理服务当成一个无状态微服务,前面挂个负载均衡,后面 N 个 GPU 节点轮询分发,认为"请求打到哪台都一样"。这套在 Web 时代是金科玉律——无状态服务随便扩缩,负载均衡自动分流,坏一台摘一台,完美。
翻车现场:上线一周后开始出现莫名其妙的性能问题。第一个症状是 TTFT 忽高忽低,同样的请求有时 600ms 有时 3 秒,毫无规律。第二个症状是某些节点频繁 OOM,而另一些节点 GPU 利用率只有 20%。第三个症状更诡异——某些多轮对话功能偶尔会"答非所问",好像丢了上下文。
根因:他们忽略了大模型对话的请求不是无状态的。每个请求强依赖"会话上下文"和"KV Cache 驻留位置"。轮询负载均衡不懂这些,同一个会话连续两轮被打到两台不同机器,第二台要把整个上下文重新 prefill 一遍——KV Cache 完全没复用,prefill 成本翻倍,TTFT 直接翻倍。更糟的是随机分发导致某台机器同时承接了多个长上下文请求,显存被打爆 OOM;而隔壁机器却闲着。至于"答非所问",是因为某些会话依赖服务端缓存的中间状态(Agent 工具调用的结果),轮询后状态对不上。
正确做法:核心是会话亲和路由(Session Affinity),让同一会话的请求尽量落到同一台或同一组持有其上下文的节点,复用 KV Cache。调度器要拿到每台机器的实时显存占用,按显存余量而非简单轮询分发。必要时用 PagedAttention 或分布式 KV Cache,让上下文可以跨节点共享,弱化亲和要求(本系列 vLLM 教程会深讲)。
这个反模式的教训是:"无状态水平扩容"是 Web 时代的红利,在大模型对话里必须升级为"带状态感知的弹性调度"。 第四章会专门拆解怎么做。
第二个案例,是一个在大促时翻车的限流策略。
场景:团队在网关层挂了一个全局令牌桶,按总 QPS 限流,超过阈值的部分直接返回 429。这套在 Web 时代也基本够用——顶不住就拒绝,保护后端不被压垮。
翻车现场:大促峰值一来,问题集中爆发。首先是付费用户也被 429——令牌桶不区分用户优先级,峰值时免费用户的洪流把令牌抢光,付费用户同样拿到 429,直接引发客诉和退款,损失最大的恰恰是高价值用户。接着是雪崩式重试——客户端拿到 429 后按固定间隔重试,重试流量和正常流量叠加,把本就紧张的网关彻底压垮,典型的重试风暴,系统在"过载-稍微恢复-再次过载"里震荡。最伤体验的是流式连接被中途 429 断开,用户看到回答一半就停了,这比直接报错还糟。
根因:单一令牌桶犯了三个错——不分优先级(付费和免费一视同仁)、不分请求类型(首字请求和流式续传、Agent 重请求和简单问答混在一起)、粗暴 429 而没有优雅降级。它本质上是把"网关层该做的优先级和降级"甩给了客户端,让用户承担了过载的代价。
正确做法:分层限流是基础——用户级(按 UID 限流)+ 租户级(按企业配额)+ 全局级(保网关)三层叠加,互为兜底。优先级队列与加权公平,付费用户、关键请求权重更高,过载时优先保障。但最关键的转变是——限流的目的不是"拒绝流量",而是"在过载时仍交付最大可能的可用性"。过载时不是直接 429,而是向推理层施加背压、降级到小模型或缓存兜底,让用户"慢一点但有"而不是"直接没有"。重试要带抖动与退避,服务端通过 Retry-After 头指导客户端指数退避加随机抖动,避免重试风暴。这些在第二章会详细拆解。
第三个案例,是一个成本始终降不下来的缓存层。
场景:团队上线了缓存层,但只做"请求字符串完全一致才命中"的精确缓存。他们的判断是"用户问题千差万别,命中率不可能高,语义缓存是花架子,不值得投入"。
翻车现场:缓存命中率长期低于 1%,几乎等于没有。本可被缓存命中的重复意图请求,全部打到 GPU,推理成本始终降不下来,单请求成本长期高于基线。高峰期算力不够,被迫扩容,TCO(总拥有成本)失控。最讽刺的是,他们花了大量精力优化 GPU 利用率、调 batch 参数,却忽略了最有效的降本杠杆。
根因:低估了"语义等价"的缓存价值。在大模型对话里,意图相同的请求比例远比想象的高——尤其是 FAQ 类、客服类、通用知识类场景。"今天天气怎么样""今天天气如何""今天天气好不好",精确匹配全部 miss,但语义缓存能全部命中。只做精确匹配,等于放弃了最大的降本杠杆。
正确做法:语义缓存,把请求 embedding 后按相似度匹配,命中"问法不同但语义等价"的历史响应。合理配置下,命中率可达 15% 到 30%。分级缓存策略,高确定性场景(事实性问答)激进缓存,低确定性场景(个性化、时效性强)保守或不缓存。缓存还要与 TTL、版本联动,模型升级、prompt 模板变更时要有失效机制,避免吐出旧版本的过期答案。第三章会详述。
这个反模式告诉我们:在大模型对话里,缓存不是"锦上添花",而是"高并发能否打平成本的胜负手"。 命中率每提升 10 个百分点,单请求成本就下降约 8% 到 10%。
把这三个失败案例放一起看,共性非常明显——没有一个是技术选型不够新,全是"分层错位"或"口径混乱":
绝大多数生产事故,根子都出在这种地方,而不是某个框架不够快、某个算法不够新。这正是本书坚持"先把分层和口径讲透"的原因——地基不牢,上面盖什么都会塌。后面四章的所有方案,本质上都是在帮你在正确的层、用正确的口径解决问题。
至此第一章完成。我们校准了 QPS 口径(1.1)、立起了指标标尺(1.2)、看过了失败的镜像(1.3)。从第二章起,正式进入"怎么盖"——从流量进入的第一道关口"接入与限流层"开始动手。