From 10b6de062269c34e2459827d45cd6804cc0b05f0 Mon Sep 17 00:00:00 2001 From: Nelson <1475262689@qq.com> Date: Wed, 8 Jul 2026 16:48:51 +0800 Subject: [PATCH] Document concurrency optimization plan --- ...026-07-08-concurrency-optimization-plan.md | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 ai_knowledge_base_v2/development_records/2026-07-08-concurrency-optimization-plan.md diff --git a/ai_knowledge_base_v2/development_records/2026-07-08-concurrency-optimization-plan.md b/ai_knowledge_base_v2/development_records/2026-07-08-concurrency-optimization-plan.md new file mode 100644 index 0000000..6d375c2 --- /dev/null +++ b/ai_knowledge_base_v2/development_records/2026-07-08-concurrency-optimization-plan.md @@ -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. 真实流式前,用户端虽然能看到排队状态,但进入生成后仍要等完整模型结果才能输出内容。