AI时代的系统架构演进从传统Web到大模型驱动系统一、传统 Web 系统结构一个典型的 Web 架构链路如下Client → CDN → Load Balancer → API Gateway → Application Server → Cache → Database各层核心作用层级作用典型技术CDN静态资源缓存将内容分发到离用户最近的边缘节点降低首屏延迟Cloudflare、AWS CloudFrontLoad Balancer将流量分发到多个后端实例避免单点故障提升可用性Nginx、HAProxy、AWS ALBAPI Gateway统一入口负责鉴权、限流、路由转发、协议转换Kong、APISIX、Spring GatewayApplication承载核心业务逻辑处理请求并返回响应Express、NestJS、Spring BootCache热点数据缓存拦截重复查询减少数据库压力Redis、MemcachedDatabase数据持久化保证 ACID 特性PostgreSQL、MySQL、MongoDB核心优化方向延迟优化CDN 缓存静态资源、Redis 缓存热点数据让请求在到达数据库之前就被拦截返回吞吐优化通过 Load Balancer 实现横向扩展增加实例数量即可线性提升处理能力可用性多实例部署 健康检查 自动摘除故障节点任何单点宕机不影响整体服务一致性数据库事务保证数据正确性缓存失效策略保证数据最终一致传统 Web 的核心假设是请求是短暂的。客户端发一个 HTTP 请求服务端在毫秒级返回响应连接随即释放。整个架构都围绕如何在单位时间内处理更多短请求QPS来设计。二、AI 时代的大模型系统架构AI 系统的本质变化从短请求-响应 → “长连接 长任务 异步执行”传统 Web 里一个请求 50ms 就结束了但大模型的一次推理可能要 5 秒甚至 60 秒。如果还用同步模型一个请求就会占住一个线程/连接几十秒系统很快就会被拖垮。因此 AI 系统必须从根本上改变架构模型。新一代架构模型Client ↓ Connection LayerWebSocket / SSE负责挂着等结果 ↓ API / Orchestrator接收请求创建任务 ↓ Queue任务排队削峰填谷 ↓ Worker Pool从队列取任务控制并发 ↓ Model LayerLLM / 图像 / 视频模型真正干活的地方 ↓ Pub/Sub结果产出后广播通知 ↓ Connection Layer找到对应用户的连接推送结果 ↓ Client这个架构的精髓在于请求的发起和结果的返回走的是两条独立的路径。客户端发请求后不会傻等而是通过长连接挂在那里等 Worker 处理完后通过 Pub/Sub 把结果推回来。三、架构核心变化本质维度传统 WebAI 系统为什么变了请求模型同步请求-响应异步请求-排队-回调模型推理耗时太长同步会阻塞所有资源生命周期毫秒级50~200ms秒/分钟级1~60sLLM 的 token 生成是逐个的本质上就慢通信方式短连接HTTP长连接WebSocket/SSE需要持续接收流式输出短连接做不到核心瓶颈QPS每秒请求数连接数 任务调度效率瓶颈从处理速度转移到等待管理一句话概括这个本质变化传统 Web 优化的是多快能处理完一个请求AI 系统优化的是同时能挂住多少个等待中的任务。四、关键瓶颈点分析1. 长连接WebSocket / SSE每个长连接都会占用服务端资源不像短连接用完就释放文件描述符FD耗尽Linux 默认每个进程只能打开 1024 个 FD每个连接占一个 FD。如果不调整ulimit1024 个用户同时在线就会把服务打挂内存占用每个连接需要维护读写缓冲区、状态信息大约占 30~80KB。1 万连接就是 300MB~800MB 的内存开销单机连接上限即使调大 FD 限制受限于内存和 CPU 调度能力单机通常在 1万~5万 连接就是极限。超过这个数就必须横向扩展2. 模型调用瓶颈模型调用是整个链路中最贵、最慢、最受限的环节高延迟GPT-4 级别的模型一次完整响应通常需要 3~30 秒复杂推理甚至超过 60 秒。这不是网络延迟而是模型本身的计算时间强限流Rate LimitAPI 提供商会限制每分钟的请求数和 token 数。比如 OpenAI 的 GPT-4 可能限制 500 RPM每分钟请求数超了直接返回 429 错误高成本GPT-4 的输入 $30/M tokens输出 $60/M tokens。一个带上下文的对话一次可能消耗数千 token成本是传统 API 调用的成百上千倍3. 任务堆积Queue Backlog当用户请求的速度生产速度超过 Worker 处理的速度消费速度时队列会不断膨胀。这会导致用户等待时间从秒级退化到分钟级延迟爆炸队列占用的内存/磁盘持续增长更早的任务排在前面后来的用户体验急剧恶化这就是经典的背压Backpressure问题下游处理不过来上游还在不停塞数据进来。4. 流式输出压力大模型的响应是逐 token 生成的前端需要实时展示打字机效果。这带来独特的压力高频 flush如果每生成一个 token 就 flush 一次网络缓冲区假设每秒生成 30 个 token就是每秒 30 次网络写入TCP 小包过多每次 flush 发送的数据可能只有几个字节产生大量 TCP 小包触发 Nagle 算法延迟或产生网络开销CPU 序列化压力每次 flush 都需要将数据序列化JSON 编码、写入缓冲区、触发系统调用高并发下 CPU 占用显著5. 上下文成本隐性瓶颈这是最容易被忽视但影响最大的瓶颈token 增长多轮对话中每轮都要把之前的对话历史作为上下文传给模型。10 轮对话后上下文可能从 500 token 膨胀到 5000 token成本线性增长上下文越长每次调用的费用越高。而且输入 token 也计费相当于重复为历史对话买单延迟线性增长模型处理更长的输入需要更多时间用户会明显感觉到对话越久回复越慢应对策略包括上下文摘要压缩、滑动窗口截断、关键信息提取后丢弃原文等。五、瓶颈解决方案核心1. 长连接WS / SSE解决方案核心思想连接层与执行层必须解耦。传统架构中处理请求的服务器同时负责维护连接。但在 AI 场景下一个任务可能执行 30 秒如果执行层还要负责维护连接资源利用率极低。所以必须把谁跟用户保持连接和谁去调模型干活拆成两个独立的服务。架构设计Connection Server多节点只负责维持连接 ↕ Pub/SubRedis / NATS消息桥梁 Worker任意节点只负责执行任务关键技术点多连接节点横向扩展单个 Connection Server 大约能维持 1 万个长连接。需要支持 10 万用户同时在线部署 10 台 Connection Server前面用 Load Balancer 分流即可。每台只做连接维护不做业务逻辑所以非常轻量。Pub/Sub 消息分发Worker 处理完任务后不需要知道用户连接在哪台 Connection Server 上。它只需要把结果publish到以userId为 key 的频道。所有 Connection Server 都subscribe了这个频道持有该用户连接的那台服务器收到消息后推送给客户端。这就是找人的过程。无状态连接层Connection Server 不存储任何业务状态不绑定任务执行。它只做两件事接收客户端连接、将 Pub/Sub 的消息推送给对应连接。这意味着任何一台 Connection Server 挂了用户重连到另一台即可不会丢失任务进度因为任务状态在 Worker 和 Queue 那边。心跳机制长连接如果长时间没有数据传输中间的网络设备NAT、防火墙、代理可能会主动断开。所以需要定期发送ping帧保活。通常每 30 秒一次心跳就够了。连接控制限制单个用户的最大连接数通常 1~3 个防止恶意用户或 bug 导致的连接泄漏耗尽服务端资源。2. 模型调用解决方案核心队列 Worker Pool限并发调用模型用户请求 → Queue排队等候→ Worker Pool取出任务控制最大并发数为 N→ 模型 API并发控制根据模型 API 的 Rate Limit 设定 Worker Pool 的最大并发数。比如 OpenAI 限制 500 RPM你的 Worker Pool 就不应该超过 ~8 个并发500/60≈8。超出的请求在队列里等而不是直接打到模型 API 被 429 拒绝。Backpressure背压设定队列最大长度。当队列已满比如超过 1000 个待处理任务直接拒绝新请求并返回友好提示“系统繁忙请稍后重试”而不是无限堆积导致所有人的延迟都变得不可接受。这是一个有损但可控的策略——宁可拒绝少数人也不能让所有人都卡住。连接复用Worker 调用模型 API 时使用 HTTP Keep-Alive 和连接池复用 TCP 连接。每次调用都新建 TCP 连接的话TLS 握手就要消耗 50~100ms高频调用下这是不可忽视的开销。3. 流式输出优化错误做法每生成一个 token 就立刻 flush 到客户端。假设每秒 30 token就是每秒 30 次网络写入大量 TCP 小包效率极低。正确做法批量 flush。设定一个时间窗口比如 50ms或 token 数量阈值比如每 5 个 token攒够了再一次性 flush。用户感知的延迟只增加了 50ms人类感知不到但网络效率提升了一个数量级。// 伪代码示意 buffer [] timer setInterval(50ms, () { if (buffer.length 0) { flush(buffer) buffer [] } }) onToken(token) { buffer.push(token) if (buffer.length 5) { flush(buffer) buffer [] } }六、WebSocket vs SSE重点对比核心区别维度WebSocketSSE通信方向双向客户端和服务端都能主动发消息单向只有服务端能推送给客户端协议独立的ws://协议通过 HTTP Upgrade 握手建立基于标准 HTTP使用text/event-stream内容类型复杂度高需要处理握手、帧解析、心跳、重连低就是一个不关闭的 HTTP 响应浏览器原生支持自动重连无需要自己实现重连逻辑有EventSourceAPI 自带断线重连还能通过Last-Event-ID恢复适用场景需要双向通信的强交互场景服务端单向推送的场景AI 场景推荐SSE默认推荐适用于大多数 AI 对话场景ChatGPT 类对话、流式文本输出、单向数据流。原因很简单——AI 对话本质上就是客户端发一个问题服务端流式返回答案这是一个单向推送模型SSE 完全够用而且实现成本远低于 WebSocket。WebSocket适用于需要客户端频繁主动发消息的场景实时打断生成用户说停换个方向、多 Agent 协作实时同步、高频双向交互如协同编辑 AI 辅助。关于打断生成这是选择 WebSocket 还是 SSE 时最常被讨论的功能WebSocket 方案在同一条连接上直接发送{ type: cancel, taskId: xxx }消息服务端立即收到并中止生成。延迟极低体验流畅SSE 方案SSE 连接是单向的客户端无法在上面发消息。需要额外发一个POST /api/cancelHTTP 请求来通知服务端。功能上完全可行但多了一次 HTTP 往返且实现上需要保证 cancel 请求能路由到正确的 Worker结论如果你的产品只需要基础的对话流式输出用 SSE。如果需要打断、实时控制等复杂交互用 WebSocket。七、SSE 高并发瓶颈与优化虽然 SSE 实现简单但在高并发下同样面临挑战瓶颈分析连接数限制SSE 本质上还是一个不关闭的 TCP 连接同样受 FD、内存的约束。浏览器对同一域名的 HTTP/1.1 连接数还有 6 个的限制HTTP/2 下多路复用可以缓解。HTTP 长连接占用SSE 连接会一直占住 Web 服务器的一个 worker/线程。如果用 Apache 这种每连接一线程的模型几百个 SSE 连接就能把服务器打满。必须用 Nginx、Node.js 这种事件驱动模型。代理缓冲问题Nginx 等反向代理默认会缓冲响应体等攒够一定量再转发给客户端。这会导致流式数据被卡住用户看不到实时输出。必须显式关闭缓冲。flush 频率和前面讨论的一样过于频繁的 flush 产生大量小包。重连风暴网络抖动时如果所有客户端同时尝试重连会产生瞬时的连接洪峰可能直接压垮服务端。优化方案关闭代理缓冲Nginx 配置proxy_buffering off; X-Accel-Buffering: no;这两行配置确保 Nginx 不缓冲 SSE 响应数据到了就立即转发。批量 flush前面已经讨论过每 50ms 或每 N 个 token flush 一次而不是逐 token flush。心跳保活SSE 协议支持注释行作为心跳格式是: ping\n\n。定期发送每 15~30 秒防止中间设备因超时断开连接。重连退避Exponential Backoff客户端重连时不要立即重试而是按指数退避1s → 2s → 4s → 8s加上随机抖动jitter。这样即使大量客户端同时断开重连请求也会被分散到一个时间窗口内避免雪崩。连接分层将 Connection Layer 独立部署为专门的服务和业务逻辑服务分开。这样 Connection 服务可以选用最适合长连接的技术栈如 Node.js而业务服务可以用最适合业务逻辑的技术栈如 Java/Python。八、不同层的技术选型语言 框架Connection Layer连接层技术适用场景理由Node.js推荐大多数场景事件驱动、单线程非阻塞 IO天然适合高并发连接。单进程轻松维持上万连接配合ws或socket.io库生态成熟Go需要更高性能goroutine 模型天然支持高并发内存占用比 Node.js 更低适合连接数极大10万的场景Rust极限场景零成本抽象 异步运行时tokio单机可以推到百万级连接但开发成本高通常只在基础设施层使用连接层的核心特征是IO 密集而非计算密集——它不做复杂计算只是维持大量连接并转发数据。所以事件驱动模型Node.js或协程模型Go是最佳选择。Orchestrator调度层技术适用场景理由Node.jsNestJS快速迭代TypeScript 全栈统一NestJS 提供了依赖注入、模块化等企业级能力JavaSpring大型团队Spring 生态成熟适合需要复杂事务管理、微服务治理的场景调度层的职责是接收请求 → 验证参数 → 创建任务 → 写入队列 → 返回 taskId。逻辑相对简单选型主要看团队技术栈。Worker Layer执行层技术适用场景理由Python首选AI 相关任务AI 生态的绝对主力LangChain、LlamaIndex、HuggingFace Transformers、向量数据库客户端等几乎都是 Python 优先。RAG、Embedding、Agent 编排等任务用 Python 开发效率最高Node.js简单的 API 转发如果 Worker 只是调用外部模型 API 然后转发结果Node.js 也能胜任Model Layer模型层外部 APIOpenAI、Claude、Gemini 等。优势是零运维按量计费劣势是受 Rate Limit 约束且数据离开了你的控制范围本地部署需要 GPU 服务器使用 vLLM、TGI 等推理框架。优势是完全可控无 Rate Limit劣势是成本高需要 GPU 运维能力中间件选型类型推荐技术说明Cache / PubSubRedis兼具缓存和发布订阅能力单机十万级 QPSAI 系统的标配QueueBullMQNode.js 生态/Kafka大规模BullMQ 基于 Redis轻量好用Kafka 适合日均千万级消息的大规模场景网关Nginx反向代理、负载均衡、SSL 终止配置灵活且性能优异九、最终架构总结连接层负责挂着Worker 负责干活队列负责排队Pub/Sub 负责找人这四个角色的分工就是 AI 系统架构的精髓。每一层都可以独立扩展连接多了加 Connection Server任务多了加 Worker流量波动大就加长队列缓冲。本质变化维度传统系统AI 系统驱动模型请求驱动来一个处理一个任务驱动来一个排一个异步处理执行模式同步请求线程等到处理完异步请求线程立即释放结果异步推送优化焦点QPS单位时间处理更多请求连接管理 任务调度挂住更多人调度更多任务架构核心原则解耦连接、任务、执行资源让系统可以独立扩展这是一切设计决策的根基。当你面对任何架构选择时问自己这个决策是在让系统更解耦还是在引入耦合解耦意味着每一层可以按自己的节奏扩展和演进耦合意味着牵一发而动全身。十、最终认知升级重点当你评估一个 AI 系统的承载能力时不要再问“QPS 能扛多少”要问能扛多少并发连接→ 决定了同时在线用户数的上限能处理多少并发任务→ 决定了 Worker Pool 的吞吐能力队列是否可控→ 队列长度是否有上限是否有背压机制延迟是否在可接受范围内模型资源是否被合理利用→ Rate Limit 是否被充分利用但不超限是否有连接复用和缓存命中