尧图精选

上传一个 200MB 的 PDF,进度条为什么能实时更新?

🕒 发布时间:2026/10/1 19:56:29 📁 来源:尧图网络
上传一个 200MB 的扫描 PDF 到知识库后台可能要经历解析 → OCR → 版面检测 → 切块 → Embedding → 索引写入。整个过程可能持续几分钟。问题是这些任务通常跑在 Celery Worker 里而前端连接的是 API。Worker、API、浏览器根本不在同一个进程进度条怎么实时更新在有谷大脑的知识库入库流程中我们采用的是PostgreSQL 存真实进度 → Redis Stream 跨进程传递 → SSE 推送浏览器。图Worker、API 与浏览器之间的进度传递链路为什么不能直接记录一个 progress假设 Worker 正在解析 PDFprogress 40% stage chunking如果这个状态只存在 Worker 内存里API 根本看不到。多副本部署时更明显Worker API A API B API C用户的 SSE 连接可能在 API B而任务可能运行在另一台机器的 Worker 上。所以这里真正需要解决三个问题进度存在哪里Worker 怎么告诉 APIAPI 怎么实时告诉浏览器PostgreSQL保存真实进度每次任务进入新阶段Worker 先把事件写入数据库{document_id:doc_123,stage:chunking,progress:55,seq:18}例如queued parsing ocr chunking embedding indexing done error这里数据库的作用不是“加速”而是保存真实状态。即使 Redis 重启、API 重启或者用户刷新页面任务执行到了哪里仍然可以恢复。所以DB 是状态真相源。Redis Stream把 Worker 的进度送到 API数据库解决了“状态不能丢”但如果 API 不断查数据库实时性就变成了轮询。所以 Worker 写完 DB 后再发布一条 Redis Stream 事件Worker ↓ PostgreSQL ↓ Redis Stream ↓ API为什么不用 Redis Pub/Sub因为 Pub/Sub 更像广播订阅者不在线这段时间的消息可能就错过了。而 Stream 会保留事件记录更适合这种有明确顺序的任务状态parsing → ocr → chunking → embedding → doneRedis 在这里不是最终存储而是一个实时传输层。SSE最后把进度推到浏览器API 收到 Stream 中的新事件后通过 SSE 推给浏览器。前端建立连接constsourcenewEventSource(/api/documents/${documentId}/rag-progress);随后不断收到id: 18 event: progress data: {stage:chunking,progress:55}前端就可以把 UI 更新成正在切块 55%因此完整链路其实就是Celery Worker ↓ PostgreSQL ↓ Redis Stream ↓ API SSE ↓ Browser断线了怎么办这里还有最后一个问题。用户切换网络、刷新页面或者电脑休眠都可能导致 SSE 断开。所以每条进度事件都有一个递增的seq15 parsing 16 ocr 17 ocr 18 chunking 19 embedding假设浏览器收到seq 17后断线。重新连接时带上last_seq 17服务端查询WHEREdocument_id?ANDseq17ORDERBYseqASC先把 18、19……补回来再继续接收实时事件。这样进度就不会因为刷新页面突然归零。seq还可以用于去重同一个事件重复收到只处理一次避免进度条来回跳动。最后这套设计最重要的不是“用了 Redis”或者“用了 SSE”而是把三件事情分开PostgreSQL 权威状态 Redis Stream 跨进程实时传递 SSE 推送到浏览器DB 保证不丢Stream 保证及时SSE 负责最后一公里。我们在有谷大脑处理大文件入库时最终发现一个看似简单的进度条真正需要解决的不是“怎么从 0% 动到 100%”。而是任务跨进程、跨服务甚至经历断线以后用户看到的进度还能不能连续、可信。这才是长耗时异步任务进度系统真正需要解决的问题。如果想进一步了解这些能力在产品里的实际应用可以体验有谷大脑https://brain.yogu.pro
上一篇/下一篇内容由系统自动关联 返回资讯列表 →