FastAPI BackgroundTasks 进阶指南
1. 核心机制解析
graph TD
A[客户端请求] --> B[FastAPI路由处理]
B --> C{是否包含BackgroundTasks}
C -->|是| D[注册后台任务]
C -->|否| E[直接返回响应]
D --> F[构造响应对象]
F --> G[返回HTTP响应]
G --> H[执行注册的后台任务]
关键设计特点:
- 任务队列机制:所有后台任务按注册顺序执行
- 自动依赖注入:通过参数声明自动获取实例
- 异常隔离:单个任务失败不影响整体流程
2. 生产级代码示例
python
# requirements.txt
fastapi==0.103.2
pydantic==2.5.3
uvicorn==0.23.2
python
from fastapi import BackgroundTasks, FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserRegister(BaseModel):
username: str
email: EmailStr
password: str
async def send_welcome_email(email: str):
"""模拟邮件发送(实际应使用SMTP库)"""
print(f"Sending email to {email}")
# 这里可以添加真正的邮件发送逻辑
@app.post("/register", status_code=201)
async def create_user(
user: UserRegister,
background_tasks: BackgroundTasks
):
"""用户注册接口"""
# 主逻辑处理
print(f"Creating user: {user.username}")
# 添加后台任务
background_tasks.add_task(
send_welcome_email,
user.email
)
return {"message": "User created successfully"}
3. 高级应用场景
3.1 数据库事务补偿
python
async def cleanup_failed_registration(user_id: int):
"""注册失败后的数据回滚"""
async with AsyncSessionLocal() as session:
await session.execute(
delete(User).where(User.id == user_id)
)
await session.commit()
@app.post("/register")
async def register_user(
user: UserRegister,
background_tasks: BackgroundTasks
):
try:
# 数据库操作...
except Exception as e:
background_tasks.add_task(
cleanup_failed_registration,
new_user.id
)
raise HTTPException(...)
3.2 任务编排模式
python
def task_wrapper(func):
"""任务执行监控装饰器"""
async def wrapper(*args, **kwargs):
try:
print(f"Starting task {func.__name__}")
await func(*args, **kwargs)
print(f"Task {func.__name__} completed")
except Exception as e:
print(f"Task failed: {str(e)}")
# 可添加重试逻辑
return wrapper
@app.post("/batch-process")
async def batch_processing(
background_tasks: BackgroundTasks
):
background_tasks.add_task(
task_wrapper(process_data)
)
background_tasks.add_task(
task_wrapper(generate_reports)
)
4. 性能优化策略
- 任务分片:将大任务拆分为多个子任务
- 资源限制:通过信号量控制并发量
- 超时设置:为长时间任务添加执行时限
python
from fastapi.concurrency import run_in_threadpool
async def resource_intensive_task():
# CPU密集型任务示例
await run_in_threadpool(
lambda: heavy_computation()
)
5. 课后 Quiz
Q1: 当后台任务需要访问数据库时,应该如何正确处理数据库会话? A) 直接使用主请求的会话 B) 每个任务创建独立会话 C) 使用全局共享会话
正确答案:B 解析:每个后台任务应该创建独立的数据库会话,因为主请求的会话在响应返回后可能已关闭。推荐在任务内部使用上下文管理器创建新会话。
6. 典型报错处理
报错现象: RuntimeError: No response returned.
原因分析: 在后台任务中直接返回了响应对象
解决方案:
- 确保后台任务函数不返回任何值
- 将响应处理逻辑移到主请求流程
- 使用独立的路由处理异步任务结果
预防建议:
- 严格区分请求处理与后台任务职责
- 使用类型检查工具验证函数返回值
- 在任务函数中添加返回值检测逻辑