55
66## 🌟 核心特性
77
8- - ✅ ** 异步处理** - 客户端立即响应(~ 100ms),无需等待处理完成
9- - ✅ ** 任务持久化** - SQLite 存储,服务重启任务不丢失
10- - ✅ ** GPU 负载均衡** - LitServe 自动调度,资源利用最优
8+ ### 高性能架构
9+ - ✅ ** Worker 主动拉取** - 0.5秒响应速度,无需调度器触发
10+ - ✅ ** 并发安全** - 原子操作防止任务重复,支持多Worker并发
11+ - ✅ ** GPU 负载均衡** - LitServe 自动调度,避免显存冲突
12+ - ✅ ** 多GPU隔离** - 每个进程只使用分配的GPU,彻底解决多卡占用
13+
14+ ### 企业级功能
15+ - ✅ ** 异步处理** - 客户端立即响应(~ 100ms),无需等待处理完成
16+ - ✅ ** 任务持久化** - SQLite 存储,服务重启任务不丢失
1117- ✅ ** 优先级队列** - 重要任务优先处理
12- - ✅ ** 实时查询** - 随时查看任务进度和状态
18+ - ✅ ** 自动清理** - 定期清理旧结果文件,保留数据库记录
19+
20+ ### 智能解析
21+ - ✅ ** 双解析器** - PDF/图片用 MinerU(GPU加速), Office/HTML等用 MarkItDown(快速)
22+ - ✅ ** 内容获取** - API自动返回 Markdown 内容,支持图片上传到 MinIO
1323- ✅ ** RESTful API** - 支持任何编程语言接入
14- - ✅ ** 智能解析器** - PDF/图片用 MinerU,其他所有格式用 MarkItDown
15- - ✅ ** 内容获取** - 获取解析后的 Markdown 内容,支持图片上传到 MinIO
24+ - ✅ ** 实时查询** - 随时查看任务进度和状态
1625
1726## 🏗️ 系统架构
1827
1928```
2029客户端请求 → FastAPI Server (立即返回 task_id)
2130 ↓
22- SQLite 任务队列
31+ SQLite 任务队列 (并发安全)
2332 ↓
24- Task Scheduler (调度器 )
33+ LitServe Worker Pool (主动拉取 + GPU自动负载均衡 )
2534 ↓
26- LitServe Worker Pool (GPU自动负载均衡)
35+ MinerU / MarkItDown 解析
2736 ↓
28- MinerU 核心处理
37+ Task Scheduler (可选监控组件)
2938```
3039
40+ ** 架构特点** :
41+ - ✅ ** Worker 主动模式** : Workers 持续循环拉取任务,无需调度器触发
42+ - ✅ ** 并发安全** : SQLite 使用原子操作防止任务重复处理
43+ - ✅ ** 自动负载均衡** : LitServe 自动分配任务到空闲 GPU
44+ - ✅ ** 智能解析** : PDF/图片用 MinerU,其他格式用 MarkItDown
45+
3146## 🚀 快速开始
3247
3348### 1. 安装依赖
@@ -86,18 +101,24 @@ curl http://localhost:8000/api/v1/tasks/{task_id}?upload_images=true
86101
87102```
88103mineru_tianshu/
89- ├── task_db.py # 数据库管理
90- ├── api_server.py # API 服务器
91- ├── litserve_worker.py # Worker Pool (MinerU + MarkItDown )
92- ├── task_scheduler.py # 任务调度器
104+ ├── task_db.py # 数据库管理 (并发安全,支持清理)
105+ ├── api_server.py # API 服务器 (自动返回内容)
106+ ├── litserve_worker.py # Worker Pool (主动拉取 + 双解析器 )
107+ ├── task_scheduler.py # 任务调度器 (可选监控)
93108├── start_all.py # 启动脚本
94109├── client_example.py # 客户端示例
95110└── requirements.txt # 依赖配置
96111```
97112
113+ ** 核心组件说明** :
114+ - ` task_db.py ` : 使用原子操作保证并发安全,支持旧任务清理
115+ - ` api_server.py ` : 查询接口自动返回Markdown内容,支持MinIO图片上传
116+ - ` litserve_worker.py ` : Worker主动循环拉取任务,支持MinerU和MarkItDown双解析
117+ - ` task_scheduler.py ` : 可选组件,仅用于监控和健康检查(默认5分钟监控,15分钟健康检查)
118+
98119## 📚 使用示例
99120
100- ### 示例 1: 提交任务并等待结果
121+ ### 示例 1: 提交任务并等待结果 (新版本 - 自动返回内容)
101122
102123``` python
103124import requests
@@ -119,14 +140,18 @@ while True:
119140 result = response.json()
120141
121142 if result[' status' ] == ' completed' :
122- # 任务完成,自动返回解析内容
143+ # v2.0 新特性: 任务完成后自动返回解析内容
123144 if result.get(' data' ):
124145 content = result[' data' ][' content' ]
125146 print (f " ✅ 解析完成,内容长度: { len (content)} 字符 " )
147+ print (f " 解析方法: { result[' data' ].get(' parser' , ' Unknown' )} " )
126148
127149 # 保存结果
128150 with open (' output.md' , ' w' , encoding = ' utf-8' ) as f:
129151 f.write(content)
152+ else :
153+ # 结果文件已被清理
154+ print (f " ⚠️ 任务完成但结果文件已清理: { result.get(' message' , ' ' )} " )
130155 break
131156 elif result[' status' ] == ' failed' :
132157 print (f " ❌ 失败: { result[' error_message' ]} " )
@@ -136,25 +161,29 @@ while True:
136161 time.sleep(2 )
137162```
138163
139- ### 示例 2: 图片上传到 MinIO
164+ ### 示例 2: 图片上传到 MinIO (可选功能)
140165
141166``` python
142167import requests
143168
144169task_id = " your-task-id"
145170
146- # 查询状态并上传图片到 MinIO
171+ # v2.0: 查询时自动返回内容,同时可选上传图片到 MinIO
147172response = requests.get(
148173 f ' http://localhost:8000/api/v1/tasks/ { task_id} ' ,
149- params = {' upload_images' : True }
174+ params = {' upload_images' : True } # 启用图片上传
150175)
151176
152177result = response.json()
153178if result[' status' ] == ' completed' and result.get(' data' ):
154- # 图片已替换为 MinIO URL
179+ # 图片已替换为 MinIO URL (HTML img 标签格式)
155180 content = result[' data' ][' content' ]
156- print (f " ✅ 图片已上传: { result[' data' ][' images_uploaded' ]} " )
181+ images_uploaded = result[' data' ][' images_uploaded' ]
182+
183+ print (f " ✅ 图片已上传到 MinIO: { images_uploaded} " )
184+ print (f " 内容长度: { len (content)} 字符 " )
157185
186+ # 保存包含 MinIO 图片链接的 Markdown
158187 with open (' output_with_cloud_images.md' , ' w' , encoding = ' utf-8' ) as f:
159188 f.write(content)
160189```
@@ -202,17 +231,30 @@ python client_example.py priority # 优先级队列
202231python start_all.py [选项]
203232
204233选项:
205- --output-dir PATH 输出目录 (默认: /tmp/mineru_tianshu_output)
206- --api-port PORT API端口 (默认: 8000)
207- --worker-port PORT Worker端口 (默认: 9000)
208- --accelerator TYPE 加速器类型: auto/cuda/cpu/mps (默认: auto)
209- --workers-per-device N 每个GPU的worker数 (默认: 1)
210- --devices DEVICES 使用的GPU设备 (默认: auto,使用所有GPU)
234+ --output-dir PATH 输出目录 (默认: /tmp/mineru_tianshu_output)
235+ --api-port PORT API端口 (默认: 8000)
236+ --worker-port PORT Worker端口 (默认: 9000)
237+ --accelerator TYPE 加速器类型: auto/cuda/cpu/mps (默认: auto)
238+ --workers-per-device N 每个GPU的worker数 (默认: 1)
239+ --devices DEVICES 使用的GPU设备 (默认: auto,使用所有GPU)
240+ --poll-interval SECONDS Worker拉取任务间隔 (默认: 0.5秒)
241+ --enable-scheduler 启用可选的任务调度器 (默认: 不启动)
242+ --monitor-interval SECONDS 调度器监控间隔 (默认: 300秒=5分钟)
243+ --cleanup-old-files-days N 清理N天前的结果文件 (默认: 7天, 0=禁用)
211244```
212245
246+ ** 新增功能说明** :
247+ - ` --poll-interval ` : Worker空闲时拉取任务的频率,默认0.5秒响应极快
248+ - ` --enable-scheduler ` : 是否启动调度器(可选),仅用于监控和健康检查
249+ - ` --monitor-interval ` : 调度器日志输出频率,建议5-10分钟避免刷屏
250+ - ` --cleanup-old-files-days ` : 自动清理旧结果文件但保留数据库记录
251+
213252### 配置示例
214253
215254``` bash
255+ # 基础启动(推荐)
256+ python start_all.py
257+
216258# CPU模式(无GPU或测试)
217259python start_all.py --accelerator cpu
218260
@@ -222,8 +264,24 @@ python start_all.py --accelerator cuda --workers-per-device 2
222264# 指定GPU: 只使用GPU 0和1
223265python start_all.py --accelerator cuda --devices 0,1
224266
225- # 自定义端口
226- python start_all.py --api-port 8080 --worker-port 9090
267+ # 启用监控调度器(可选)
268+ python start_all.py --enable-scheduler --monitor-interval 300
269+
270+ # 调整Worker拉取频率(高负载场景)
271+ python start_all.py --poll-interval 1.0
272+
273+ # 禁用旧文件清理(保留所有结果)
274+ python start_all.py --cleanup-old-files-days 0
275+
276+ # 完整配置示例
277+ python start_all.py \
278+ --accelerator cuda \
279+ --devices 0,1 \
280+ --workers-per-device 2 \
281+ --poll-interval 0.5 \
282+ --enable-scheduler \
283+ --monitor-interval 300 \
284+ --cleanup-old-files-days 7
227285
228286# Mac M系列芯片
229287python start_all.py --accelerator mps
@@ -272,7 +330,16 @@ GET /api/v1/tasks/{task_id}?upload_images=false
272330
273331返回:
274332 - status: pending | processing | completed | failed
275- - data: 任务完成后返回 Markdown 内容
333+ - data: 任务完成后**自动返回** Markdown 内容
334+ - markdown_file: 文件名
335+ - content: 完整的 Markdown 内容
336+ - images_uploaded: 是否已上传图片
337+ - has_images: 是否包含图片
338+ - message: 如果结果文件已清理会提示
339+
340+ 注意:
341+ - v2.0 新特性: 完成的任务会自动返回内容,无需额外请求
342+ - 如果结果文件已被清理(超过保留期),data 为 null 但任务记录仍可查询
276343```
277344
278345### 3. 队列统计
@@ -289,6 +356,22 @@ DELETE /api/v1/tasks/{task_id}
289356只能取消 pending 状态的任务
290357```
291358
359+ ### 5. 管理接口
360+
361+ ** 重置超时任务**
362+ ``` http
363+ POST /api/v1/admin/reset-stale?timeout_minutes=60
364+
365+ 将超时的 processing 任务重置为 pending
366+ ```
367+
368+ ** 清理旧任务**
369+ ``` http
370+ POST /api/v1/admin/cleanup?days=7
371+
372+ 仅用于手动触发清理(自动清理会每24小时执行一次)
373+ ```
374+
292375## 🔧 故障排查
293376
294377### 问题1: Worker 无法启动
@@ -305,19 +388,30 @@ pip list | grep -E "(mineru|litserve|torch)"
305388
306389### 问题2: 任务一直 pending
307390
308- ** 检查调度器**
391+ > ⚠️ ** 重要** : Worker 现在是主动拉取模式,不需要调度器触发!
392+
393+ ** 检查 Worker 是否运行**
309394``` bash
310- ps aux | grep task_scheduler.py
395+ # Windows
396+ tasklist | findstr python
397+
398+ # Linux/Mac
399+ ps aux | grep litserve_worker
311400```
312401
313- ** 手动触发 **
402+ ** 检查 Worker 健康状态 **
314403``` bash
315404curl -X POST http://localhost:9000/predict \
316405 -H " Content-Type: application/json" \
317- -d ' {"action":"poll "}'
406+ -d ' {"action":"health "}'
318407```
319408
320- ### 问题3: 显存不足
409+ ** 查看数据库状态**
410+ ``` bash
411+ python -c " from task_db import TaskDB; db = TaskDB(); print(db.get_queue_stats())"
412+ ```
413+
414+ ### 问题3: 显存不足或多卡占用
321415
322416** 减少worker数量**
323417``` bash
@@ -330,6 +424,14 @@ export MINERU_VIRTUAL_VRAM_SIZE=6
330424python start_all.py
331425```
332426
427+ ** 指定特定GPU**
428+ ``` bash
429+ # 只使用GPU 0
430+ python start_all.py --devices 0
431+ ```
432+
433+ > 💡 ** 提示** : 新版本已修复多卡显存占用问题,通过设置 ` CUDA_VISIBLE_DEVICES ` 确保每个进程只使用分配的GPU
434+
333435### 问题4: 端口被占用
334436
335437** 查看占用**
@@ -343,16 +445,97 @@ lsof -i :8000
343445
344446** 使用其他端口**
345447``` bash
346- python start_all.py --api-port 8080
448+ python start_all.py --api-port 8080 --worker-port 9090
449+ ```
450+
451+ ### 问题5: 结果文件丢失
452+
453+ ** 查询任务状态**
454+ ``` bash
455+ curl http://localhost:8000/api/v1/tasks/{task_id}
456+ ```
457+
458+ ** 说明** : 如果返回 ` result files have been cleaned up ` ,说明结果文件已被清理(默认7天后)
459+
460+ ** 解决方案** :
461+ ``` bash
462+ # 延长保留时间为30天
463+ python start_all.py --cleanup-old-files-days 30
464+
465+ # 或禁用自动清理
466+ python start_all.py --cleanup-old-files-days 0
467+ ```
468+
469+ ### 问题6: 任务重复处理
470+
471+ ** 症状** : 同一个任务被多个 worker 处理
472+
473+ ** 原因** : 这不应该发生,数据库使用了原子操作防止重复
474+
475+ ** 排查** :
476+ ``` bash
477+ # 检查是否有多个 TaskDB 实例连接不同的数据库文件
478+ # 确保所有组件使用同一个 mineru_tianshu.db
347479```
348480
349481## 🛠️ 技术栈
350482
351483- ** Web** : FastAPI + Uvicorn
352- - ** 解析器** : MinerU (PDF/图片) + MarkItDown (Office/文本)
353- - ** GPU 调度** : LitServe
354- - ** 存储** : SQLite + MinIO (可选)
484+ - ** 解析器** : MinerU (PDF/图片) + MarkItDown (Office/文本/HTML等 )
485+ - ** GPU 调度** : LitServe (自动负载均衡)
486+ - ** 存储** : SQLite (并发安全) + MinIO (可选)
355487- ** 日志** : Loguru
488+ - ** 并发模型** : Worker主动拉取 + 原子操作
489+
490+ ## 🆕 版本更新说明
491+
492+ ### v2.0 重大改进
493+
494+ ** 1. Worker 主动拉取模式**
495+ - ✅ Workers 持续循环拉取任务,无需调度器触发
496+ - ✅ 默认 0.5 秒拉取间隔,响应速度极快
497+ - ✅ 空闲时自动休眠,不占用CPU资源
498+
499+ ** 2. 数据库并发安全增强**
500+ - ✅ 使用 ` BEGIN IMMEDIATE ` 和原子操作
501+ - ✅ 防止任务重复处理
502+ - ✅ 支持多 Worker 并发拉取
503+
504+ ** 3. 调度器变为可选**
505+ - ✅ 不再是必需组件,Workers 可独立运行
506+ - ✅ 仅用于系统监控和健康检查
507+ - ✅ 默认不启动,减少系统开销
508+
509+ ** 4. 结果文件清理功能**
510+ - ✅ 自动清理旧结果文件(默认7天)
511+ - ✅ 保留数据库记录供查询
512+ - ✅ 可配置清理周期或禁用
513+
514+ ** 5. API 自动返回内容**
515+ - ✅ 查询接口自动返回 Markdown 内容
516+ - ✅ 无需额外请求获取结果
517+ - ✅ 支持图片上传到 MinIO
518+
519+ ** 6. 多GPU显存优化**
520+ - ✅ 修复多卡显存占用问题
521+ - ✅ 每个进程只使用分配的GPU
522+ - ✅ 通过 ` CUDA_VISIBLE_DEVICES ` 隔离
523+
524+ ### 迁移指南 (v1.x → v2.0)
525+
526+ ** 无需修改代码** ,只需注意:
527+ 1 . 调度器现在是可选的,不启动也能正常工作
528+ 2 . 结果文件默认7天后清理,如需保留请设置 ` --cleanup-old-files-days 0 `
529+ 3 . API 查询接口现在会返回 ` data ` 字段包含完整内容
530+
531+ ### 性能提升
532+
533+ | 指标 | v1.x | v2.0 | 提升 |
534+ | -----| ------| ------| -----|
535+ | 任务响应延迟 | 5-10秒 (调度器触发) | 0.5秒 (Worker主动拉取) | ** 10-20倍** |
536+ | 并发安全性 | 基础锁机制 | 原子操作 + 状态检查 | ** 可靠性提升** |
537+ | 多GPU效率 | 有时会出现显存冲突 | 完全隔离,无冲突 | ** 稳定性提升** |
538+ | 系统开销 | 调度器持续运行 | 可选监控(5分钟) | ** 资源节省** |
356539
357540## 📝 核心依赖
358541
0 commit comments