工程架构认知(三):从传统Web系统到AI大模型驱动系统

AI时代的系统架构演进:从传统Web到大模型驱动系统

一、传统 Web 系统结构

一个典型的 Web 架构链路如下:

复制代码
Client → CDN → Load Balancer → API Gateway → Application Server → Cache → Database

各层核心作用

层级 作用 典型技术
CDN 静态资源缓存,将内容分发到离用户最近的边缘节点,降低首屏延迟 Cloudflare、AWS CloudFront
Load Balancer 将流量分发到多个后端实例,避免单点故障,提升可用性 Nginx、HAProxy、AWS ALB
API Gateway 统一入口,负责鉴权、限流、路由转发、协议转换 Kong、APISIX、Spring Gateway
Application 承载核心业务逻辑,处理请求并返回响应 Express、NestJS、Spring Boot
Cache 热点数据缓存,拦截重复查询,减少数据库压力 Redis、Memcached
Database 数据持久化,保证 ACID 特性 PostgreSQL、MySQL、MongoDB

核心优化方向

  • 延迟优化:CDN 缓存静态资源、Redis 缓存热点数据,让请求在到达数据库之前就被拦截返回
  • 吞吐优化:通过 Load Balancer 实现横向扩展,增加实例数量即可线性提升处理能力
  • 可用性:多实例部署 + 健康检查 + 自动摘除故障节点,任何单点宕机不影响整体服务
  • 一致性:数据库事务保证数据正确性,缓存失效策略保证数据最终一致

传统 Web 的核心假设是:请求是短暂的。客户端发一个 HTTP 请求,服务端在毫秒级返回响应,连接随即释放。整个架构都围绕"如何在单位时间内处理更多短请求(QPS)"来设计。

二、AI 时代的大模型系统架构

AI 系统的本质变化:

从"短请求-响应" → "长连接 + 长任务 + 异步执行"

传统 Web 里一个请求 50ms 就结束了,但大模型的一次推理可能要 5 秒甚至 60 秒。如果还用同步模型,一个请求就会占住一个线程/连接几十秒,系统很快就会被拖垮。因此 AI 系统必须从根本上改变架构模型。

新一代架构模型

复制代码
Client
  ↓
Connection Layer(WebSocket / SSE,负责"挂着"等结果)
  ↓
API / Orchestrator(接收请求,创建任务)
  ↓
Queue(任务排队,削峰填谷)
  ↓
Worker Pool(从队列取任务,控制并发)
  ↓
Model Layer(LLM / 图像 / 视频模型,真正干活的地方)
  ↓
Pub/Sub(结果产出后,广播通知)
  ↓
Connection Layer(找到对应用户的连接,推送结果)
  ↓
Client

这个架构的精髓在于:请求的发起和结果的返回走的是两条独立的路径。客户端发请求后不会傻等,而是通过长连接"挂"在那里,等 Worker 处理完后通过 Pub/Sub 把结果"推"回来。

三、架构核心变化(本质)

维度 传统 Web AI 系统 为什么变了
请求模型 同步(请求-响应) 异步(请求-排队-回调) 模型推理耗时太长,同步会阻塞所有资源
生命周期 毫秒级(50~200ms) 秒/分钟级(1~60s+) LLM 的 token 生成是逐个的,本质上就慢
通信方式 短连接(HTTP) 长连接(WebSocket/SSE) 需要持续接收流式输出,短连接做不到
核心瓶颈 QPS(每秒请求数) 连接数 + 任务调度效率 瓶颈从"处理速度"转移到"等待管理"

一句话概括这个本质变化:传统 Web 优化的是"多快能处理完一个请求",AI 系统优化的是"同时能挂住多少个等待中的任务"。

四、关键瓶颈点分析

1. 长连接(WebSocket / SSE)

每个长连接都会占用服务端资源,不像短连接用完就释放:

  • 文件描述符(FD)耗尽 :Linux 默认每个进程只能打开 1024 个 FD,每个连接占一个 FD。如果不调整 ulimit,1024 个用户同时在线就会把服务打挂
  • 内存占用:每个连接需要维护读写缓冲区、状态信息,大约占 30~80KB。1 万连接就是 300MB~800MB 的内存开销
  • 单机连接上限:即使调大 FD 限制,受限于内存和 CPU 调度能力,单机通常在 1万~5万 连接就是极限。超过这个数就必须横向扩展

2. 模型调用瓶颈

模型调用是整个链路中最贵、最慢、最受限的环节:

  • 高延迟:GPT-4 级别的模型,一次完整响应通常需要 3~30 秒,复杂推理甚至超过 60 秒。这不是网络延迟,而是模型本身的计算时间
  • 强限流(Rate Limit):API 提供商会限制每分钟的请求数和 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/Sub(Redis / 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(重点对比)

核心区别

维度 WebSocket SSE
通信方向 双向(客户端和服务端都能主动发消息) 单向(只有服务端能推送给客户端)
协议 独立的 ws:// 协议,通过 HTTP Upgrade 握手建立 基于标准 HTTP,使用 text/event-stream 内容类型
复杂度 高(需要处理握手、帧解析、心跳、重连) 低(就是一个不关闭的 HTTP 响应,浏览器原生支持)
自动重连 无(需要自己实现重连逻辑) 有(EventSource API 自带断线重连,还能通过 Last-Event-ID 恢复)
适用场景 需要双向通信的强交互场景 服务端单向推送的场景

AI 场景推荐

SSE(默认推荐) 适用于大多数 AI 对话场景:ChatGPT 类对话、流式文本输出、单向数据流。原因很简单------AI 对话本质上就是"客户端发一个问题,服务端流式返回答案",这是一个单向推送模型,SSE 完全够用,而且实现成本远低于 WebSocket。

WebSocket 适用于需要客户端频繁主动发消息的场景:实时打断生成(用户说"停,换个方向")、多 Agent 协作实时同步、高频双向交互(如协同编辑 + AI 辅助)。

关于"打断生成"

这是选择 WebSocket 还是 SSE 时最常被讨论的功能:

  • WebSocket 方案 :在同一条连接上直接发送 { type: "cancel", taskId: "xxx" } 消息,服务端立即收到并中止生成。延迟极低,体验流畅
  • SSE 方案 :SSE 连接是单向的,客户端无法在上面发消息。需要额外发一个 POST /api/cancel HTTP 请求来通知服务端。功能上完全可行,但多了一次 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 配置):

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,天然适合高并发连接。单进程轻松维持上万连接,配合 wssocket.io 库生态成熟
Go 需要更高性能 goroutine 模型天然支持高并发,内存占用比 Node.js 更低,适合连接数极大(10万+)的场景
Rust 极限场景 零成本抽象 + 异步运行时(tokio),单机可以推到百万级连接,但开发成本高,通常只在基础设施层使用

连接层的核心特征是 IO 密集而非计算密集------它不做复杂计算,只是维持大量连接并转发数据。所以事件驱动模型(Node.js)或协程模型(Go)是最佳选择。

Orchestrator(调度层)

技术 适用场景 理由
Node.js(NestJS) 快速迭代 TypeScript 全栈统一,NestJS 提供了依赖注入、模块化等企业级能力
Java(Spring) 大型团队 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(模型层)

  • 外部 API:OpenAI、Claude、Gemini 等。优势是零运维,按量计费;劣势是受 Rate Limit 约束,且数据离开了你的控制范围
  • 本地部署:需要 GPU 服务器,使用 vLLM、TGI 等推理框架。优势是完全可控,无 Rate Limit;劣势是成本高,需要 GPU 运维能力

中间件选型

类型 推荐技术 说明
Cache / PubSub Redis 兼具缓存和发布订阅能力,单机十万级 QPS,AI 系统的标配
Queue BullMQ (Node.js 生态)/ Kafka(大规模) BullMQ 基于 Redis,轻量好用;Kafka 适合日均千万级消息的大规模场景
网关 Nginx 反向代理、负载均衡、SSL 终止,配置灵活且性能优异

九、最终架构总结

连接层负责"挂着",Worker 负责"干活",队列负责"排队",Pub/Sub 负责"找人"

这四个角色的分工就是 AI 系统架构的精髓。每一层都可以独立扩展:连接多了加 Connection Server,任务多了加 Worker,流量波动大就加长队列缓冲。

本质变化

维度 传统系统 AI 系统
驱动模型 请求驱动(来一个处理一个) 任务驱动(来一个排一个,异步处理)
执行模式 同步(请求线程等到处理完) 异步(请求线程立即释放,结果异步推送)
优化焦点 QPS(单位时间处理更多请求) 连接管理 + 任务调度(挂住更多人,调度更多任务)

架构核心原则

解耦连接、任务、执行资源,让系统可以独立扩展

这是一切设计决策的根基。当你面对任何架构选择时,问自己:这个决策是在让系统更解耦,还是在引入耦合?解耦意味着每一层可以按自己的节奏扩展和演进,耦合意味着牵一发而动全身。

十、最终认知升级(重点)

当你评估一个 AI 系统的承载能力时,不要再问:

"QPS 能扛多少?"

要问:

  • 能扛多少并发连接? → 决定了同时在线用户数的上限
  • 能处理多少并发任务? → 决定了 Worker Pool 的吞吐能力
  • 队列是否可控? → 队列长度是否有上限?是否有背压机制?延迟是否在可接受范围内?
  • 模型资源是否被合理利用? → Rate Limit 是否被充分利用但不超限?是否有连接复用和缓存命中?
相关推荐
高洁015 小时前
AI算法实战:逻辑回归在风控场景中的应用
人工智能·python·深度学习·transformer
Timer@5 小时前
LangChain 教程 05|模型配置:AI 的大脑与推理引擎
人工智能·算法·langchain
C澒5 小时前
AI 生码 - PRD2CODE:Schema2PRD 全流程设计与实现
前端·ai编程
sali-tec5 小时前
C# 基于OpenCv的视觉工作流-章50-霍夫找圆
图像处理·人工智能·opencv·算法·计算机视觉
Warren2Lynch6 小时前
无缝知识发布:开发者指南——将 Visual Paradigm OpenDocs 与企业 WordPress 集成
人工智能·架构·uml
掘金者阿豪6 小时前
微信图片已过期或已被清理,真的找不回了吗?完整自救指南
前端·后端
颜酱6 小时前
从 DeepSeek 文本对话到流式输出
前端·javascript·人工智能
Flittly6 小时前
【SpringAIAlibaba新手村系列】(17)百炼 RAG 知识库应用
java·人工智能·spring boot·spring·ai
QC·Rex6 小时前
向量数据库对比与实战:从原理到生产落地
数据库·人工智能·向量数据库