扛住15w QPS,零延迟AI故事流平台后端架构实录

0 阅读4分钟

扛住15w QPS,零延迟AI故事流平台后端架构实录

项目(DramaScroll AI)上线三天,日活过了三十万,QPS 峰值 15w。

作为独立负责整个大后端架构和 AI 调度网络的开发者,我把这套架构的核心迭代过程拿出来复盘。

全栈开发经常在各个端上平均发力。但在真实的高流量压满情况下,你很快会发现真正的瓶颈都在大模型生成卡顿和数据库连库被打爆这些环节。

因此在这套架构里,前端仅仅被我当成一个纯渲染的宿主。我直接在 Vite 里挂了最新的 React 19,外加配上了一行 React Compiler 开启编译层优化,把所有的繁琐重绘管理全部交给机器处理。整体产物打包扔到 Vercel Edge 网络上之后,前端我就基本没再过问。

重头戏在服务层和 AI 架构。这花了我足足一个月的时间推倒重来。

一、 服务端:拿 Node 与 Java 双轨挡下海量连接

在普通的单台或者微服务架构下,几万个用户同时挂着看流式推送,连接池瞬间就会枯竭。为了不让核心事务 API 随风飘摇,我拆了 BFF 与 Core 的混合双轨架构。

flowchart TD
    A[ React 19 瀑布流 ] --> |长连接推送| B(Node.js Express BFF)
    B --> |低频落库/解耦| C(Java Spring Boot Core API)
    B --> |点赞写缓冲| E(Upstash Serverless Redis)
    C --> |批处理 Upsert| D[(Supabase Postgres 主库)]

1. Node BFF 生吞所有流式推送

为了做内容实时拉取,很多人脑海中第一反应是上 WebSocket。在这个不停给用户源源不断下发“吃瓜剧情事件”的只读流业务里,全双工实在是太重了。

我最终决定在 Express 侧走纯 Server-Sent Events (SSE)。充分利用 Node.js 原生的异步非阻塞特性去硬抗前端成千上万挂着的空闲长连接,将高负载死死按在 BFF 层。

router.get('/api/v1/stream/story', verifyAuthContext, async (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');

  const stream = await groqLLMService.generateStoryStream(req.query.topic);
    
  for await (const chunk of stream) {
    if (chunk.choices[0]?.delta?.content) {
      res.write(`data: ${JSON.stringify({
         content: chunk.choices[0].delta.content
      })}\n\n`);
    }
  }
  res.end();
});

2. 避免写并发把 PostgreSQL 干碎

产品里,用户除了滑屏就是疯狂触发各种“暴击/震惊/吃瓜”按钮。15w 的峰值 QPS 如果全透压给 Supabase,底层容器分分钟 OOM 给你看。

我在 Java Spring Boot 这块单独手写了一层 Write-Behind 拦截。流量打过来先进 Serverless 的 Upstash Redis 里暂存起来。到了 Java 这里的定时轮询调度器,每隔至少 5 秒才扫一圈 Redis 集群里的增量 Hash。

打包拉出来的数据,走 MyBatis 拼的 PostgreSQL 原生 ON CONFLICT DO UPDATE 大批量刷入持久层。牺牲了极微小的数据宕机风险,从底层救活了整个业务线的读写性能。

// Spring Boot 大流量写缓冲核心代码
@Scheduled(fixedRate = 5000)
@Transactional
public void flushInteractionsSnapshot() {
    Set<String> keys = redisTemplate.keys("interaction:buffer:*");
    if(keys.isEmpty()) return;
    
    // 一次性聚合所有行为入库,规避零散网络开销
    repository.batchUpsertEngagements(metricsParser.pullAndParse(keys));
    redisTemplate.delete(keys);
}

Gemini_Generated_Image_30c0tm30c0tm30c0.png

二、 AI中台:硬生生把吐字延迟压到毫秒级

市面上一堆套大模型的应用体验极其槽糕。用户点一下按钮,面对黑屏发呆个两三秒才蹦字。无限下拉流业务要是敢有一丝这种体感的延迟,第二天日活就得腰斩。

1. 抛弃 GPU 的刻板印象,面向 LPU 开发

想做实时引擎,不要指望普通云厂商按小时租的 T4 或者 A100。

我们的文本推理全部重构迁移到了硬件厂商 Groq 的 LPU (Language Processing Unit) 网络。模型选型卡死了 Llama 3 8B 这种推理天花板,放弃庞大的千亿参数模型来追求最极端的首字节反馈。真实压测,集群输出效率长期稳在 800+ tokens/s。

代码逻辑不用加这加那,完全的“火力直出流”:

const groq = new Groq({ apiKey: process.env.GROQ_API_KEY });

export async function constructPlot(topicHash: string) {
    return await groq.chat.completions.create({
        messages: [{
            role: "user",
            content: `基于标签串 ${topicHash} 生成一个高密度的剧情转折点。不要跟我废话说好的或当然,第一句直接切入核心暴点流。`
        }],
        model: "llama3-8b-8192",
        stream: true,
    });
}

2. 用 Pinecone 做基于向量空间推荐召回

平台用户每次看完一个剧情流之后,点赞或者刷过的行为会被捕获。

如果要自己从零手搓带特征工程推荐系统,成本和技术债根本算不过账。我直接采用最原始但也最稳定的语义切片模式。把用户的短期偏好调用 text-embedding-3-small 折叠成 1536 维向量储存在 Pinecone 里。 等再次触发拉取动作时,对那堆数据打一个最直接的内部 Cosine Similarity(余弦相似度)计算,抓取最高相似度的几个剧情节点作为 Prompt 给大语言模型去做定向续写。在这个体量下,精度已经准到离谱了。

三、 结案复盘与编码底牌

image.png

一整套跑下来,真正的架构演进常常是在不停地砍分支做减法。剥离低效连接,将短时数据缓存隔离,抛弃高延迟模型。

但更底层的技术爆发点在于——在这个从 0 到 1 扛住海量并发的项目里,我几乎没有手动敲一行业务代码

整个项目的实施,我深度实践了 Claude 官方强推的高效 AI 编码流: 所有的脏累活全部交给了智能体 Agent。在对接各项官方组件部署和鉴权(Supabase/Vercel 等)时,尽量引入它们最原生的 MCP (Model Context Protocol) 工具代理,让智能体自己阅读标准并自动编写部署脚手架。而在面对并发调优与疑难 Debug 时,我给智能体强制加载了多套顶级专家 Skills,用流程逼迫它们去检索源码并给出极致闭环方案。

身为这块架构的 Owner,我的精力 100% 留给了做技术选型决策与系统链路纠偏。不写代码,不仅没让我失去掌控感,反而让这个平台以数倍于常人的速度推向了百万并发验证台。

项目的全部架构细节和代码,我已经打包开源,有兴趣挑战高并发或者体验 AI 极致提效流的朋友自己去拉下来跑: 🔗 源码传送门github.com/javasbot/dr…