Skip to content

Commit 08a89ae

Browse files
committed
feat(tianshu): v2.0 架构升级 - Worker主动拉取模式
主要改进: - Worker主动拉取任务,响应速度提升10-20倍 (5-10s → 0.5s) - 数据库并发安全增强,使用原子操作防止任务重复 - 调度器变为可选监控组件,默认不启动 - 修复多GPU显存占用问题,完全隔离各进程 新增功能: - API自动返回解析内容 - 结果文件自动清理(可配置) - 支持图片上传MinIO
1 parent 484ff5a commit 08a89ae

File tree

5 files changed

+782
-251
lines changed

5 files changed

+782
-251
lines changed

projects/mineru_tianshu/README.md

Lines changed: 222 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,44 @@
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
```
88103
mineru_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
103124
import 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
142167
import requests
143168

144169
task_id = "your-task-id"
145170

146-
# 查询状态并上传图片到 MinIO
171+
# v2.0: 查询时自动返回内容,同时可选上传图片到 MinIO
147172
response = requests.get(
148173
f'http://localhost:8000/api/v1/tasks/{task_id}',
149-
params={'upload_images': True}
174+
params={'upload_images': True} # 启用图片上传
150175
)
151176

152177
result = response.json()
153178
if 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 # 优先级队列
202231
python 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或测试)
217259
python 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
223265
python 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系列芯片
229287
python 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
315404
curl -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
330424
python 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

Comments
 (0)