Document concurrency optimization plan
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
# 并发能力优化实施计划与过程记录
|
||||
|
||||
## 背景
|
||||
|
||||
当前用户端问答链路已经接入飞书知识库检索和大模型调用。随着真实用户进入,系统会面对多个慢外部依赖:
|
||||
|
||||
- 飞书 Wiki / Docx / Drive 文件读取。
|
||||
- 大模型接口调用。
|
||||
- SSE 长连接输出。
|
||||
- MySQL 聊天记录和日志写入。
|
||||
|
||||
如果没有并发保护,高峰期请求可能堆满后端 worker、线程池、数据库连接或外部 API 限额,最终表现为用户端长时间无响应、`Failed to fetch`、502/500 或服务整体变慢。
|
||||
|
||||
## 当前问题判断
|
||||
|
||||
根据当前代码现状,主要风险不是单纯 worker 数量不足,而是问答主链路仍有较多同步阻塞点:
|
||||
|
||||
1. `/chat/completions` 会先完成飞书检索和模型完整调用,再开始返回 SSE。
|
||||
2. 当前 SSE 更接近“完整答案切片输出”,不是模型真实流式输出。
|
||||
3. 飞书和模型调用使用同步 `httpx`,外部服务慢时会占住后端执行资源。
|
||||
4. 飞书缓存是进程内缓存,多 worker 后缓存不共享。
|
||||
5. 当前没有全局并发准入和排队机制,请求超过能力边界时缺少用户可理解的等待状态。
|
||||
|
||||
## 优化目标
|
||||
|
||||
一期并发优化先达成下面目标:
|
||||
|
||||
1. 高峰期不让请求无限进入飞书和模型链路。
|
||||
2. 用户端能看到“排队中 / 生成中 / 请求过多”的明确状态。
|
||||
3. 后端能通过配置控制最大并发、最大排队数和排队超时时间。
|
||||
4. 为后续 async 化、真流式、Redis 缓存和多 worker 扩展留好边界。
|
||||
|
||||
## 实施步骤
|
||||
|
||||
### 阶段 1:并发准入与排队兜底
|
||||
|
||||
目标:先保护系统,不让过量请求直接打满飞书和模型。
|
||||
|
||||
实现内容:
|
||||
|
||||
- 后端增加问答并发管理器。
|
||||
- 配置项:
|
||||
- `chat_max_active_requests`:同时进入生成链路的最大请求数。
|
||||
- `chat_max_queue_size`:允许等待的最大排队数。
|
||||
- `chat_queue_timeout_seconds`:最长排队等待时间。
|
||||
- 用户端 SSE 支持状态事件:
|
||||
- `queued`:请求已进入排队。
|
||||
- `generating`:请求获得执行名额,开始生成。
|
||||
- `content`:模型内容片段。
|
||||
- `error`:排队超时、队列满或外部服务错误。
|
||||
- 队列满时直接提示“当前请求过多,请稍后再试”。
|
||||
- 排队时不扣每日次数;进入真实生成并落库后再按原逻辑更新。
|
||||
|
||||
阶段限制:
|
||||
|
||||
- 第一阶段可以先使用进程内并发管理器,适合当前单 worker / 本地开发。
|
||||
- 多 worker 生产环境需要阶段 4 的 Redis 全局队列,否则每个 worker 只知道自己的排队状态。
|
||||
|
||||
### 阶段 2:模型真实流式输出
|
||||
|
||||
目标:用户秒见首字,减少“请求像卡死”的感觉。
|
||||
|
||||
实现内容:
|
||||
|
||||
- 模型调用支持上游 `stream=true`。
|
||||
- 后端边接收模型 chunk 边通过 SSE 推给前端。
|
||||
- 后端累计完整回答,生成结束后再写入 assistant 消息和 AI 请求日志。
|
||||
- 用户取消时尽量关闭上游模型请求。
|
||||
|
||||
### 阶段 3:飞书与模型调用 async 化
|
||||
|
||||
目标:减少同步阻塞,提高单 worker 并发承载能力。
|
||||
|
||||
实现内容:
|
||||
|
||||
- 飞书读取改为 `httpx.AsyncClient`。
|
||||
- 模型调用改为异步。
|
||||
- `/chat/completions`、RAG 构建、模型服务逐步改为 async。
|
||||
- 注意数据库仍需独立评估,避免在 async 链路里长时间持有同步 DB session。
|
||||
|
||||
### 阶段 4:Redis 全局队列与共享缓存
|
||||
|
||||
目标:支持多 worker / 多实例部署。
|
||||
|
||||
实现内容:
|
||||
|
||||
- Redis 存储全局并发计数、等待队列和取消标记。
|
||||
- Redis 缓存飞书 token、node info、children、docx/file 文本内容。
|
||||
- 多 worker 共享缓存,避免每个 worker 重复读取飞书。
|
||||
- 增加缓存 TTL 和手动清理策略。
|
||||
|
||||
### 阶段 5:worker、连接池和压测调参
|
||||
|
||||
目标:基于数据确定 worker 数和连接池配置。
|
||||
|
||||
实现内容:
|
||||
|
||||
- Docker/生产启动支持配置 worker 数。
|
||||
- 调整 SQLAlchemy pool、MySQL `max_connections` 和网关超时。
|
||||
- 压测 10 / 50 / 100 / 200 并发,记录:
|
||||
- 首字时间。
|
||||
- 总响应时间。
|
||||
- 错误率。
|
||||
- 队列等待时间。
|
||||
- 飞书读取耗时。
|
||||
- 模型调用耗时。
|
||||
- DB 连接数。
|
||||
|
||||
## 当前执行计划
|
||||
|
||||
本次先从阶段 1 开始:
|
||||
|
||||
1. 新增后端进程内并发管理器。
|
||||
2. 用户端支持排队状态提示。
|
||||
3. 将配置项加入系统设置。
|
||||
4. 验证队列满、排队中、正常生成三种路径。
|
||||
5. 记录第一阶段已完成内容和遗留限制。
|
||||
|
||||
后续再继续阶段 2 的真实模型流式输出。
|
||||
|
||||
## 风险记录
|
||||
|
||||
1. 进程内队列不是最终生产方案,多 worker 后需要 Redis。
|
||||
2. 当前模型和飞书外部 API 仍可能限流,排队只能保护本系统,不能突破外部限额。
|
||||
3. 排队请求持有 SSE 连接,网关和浏览器超时时间要一起配置。
|
||||
4. 真实流式前,用户端虽然能看到排队状态,但进入生成后仍要等完整模型结果才能输出内容。
|
||||
Reference in New Issue
Block a user