Django 项目中处理后台任务时,Celery 虽强大但引入 Redis/RabbitMQ 等外部依赖,增加了运维复杂度。django-background-tasks 作为轻量级原生方案,通过数据库持久化任务队列,实现简单可靠的异步调度,尤其适合中小型项目。
核心设计原理
该 API 以 Django ORM 为基础,将任务序列化为 JSON 存入django_background_task_task表。字段包括task_name(函数路径)、args/kwargs(参数)、schedule(Unix 时间戳)、attempts(重试计数)、status。worker 通过manage.py process_tasks轮询执行,到期任务被锁定时执行,失败则更新schedule为当前 + 指数延迟(默认 schedule *= 2,上限 300s)。
证据显示,这种 DB 轮询虽有锁竞争,但对 QPS<100 的任务队列,延迟 < 5s,远优于无队列阻塞视图。“通过数据库存储任务,后台进程轮询执行,轻量、无需额外服务。” 相比 Celery 零配置,启动仅需 migrate 表。
asyncio 集成实现
虽库核心同步,但结合 asgiref.sync_to_async 无缝支持 async 任务。定义:
from background_task import background
from asgiref.sync import sync_to_async
import asyncio
@background(schedule=10)
def async_process(data):
async def inner():
await asyncio.sleep(1) # IO密集
return process_data(data)
return asyncio.run(inner())
视图触发:async_process.delay('data'),worker 执行时 run event loop。参数:sync_to_async(async_process, thread_sensitive=True)包装,确保 ORM 安全。
落地清单:
- ASGI 服务器(uvicorn/hypercorn)。
- 任务内避免阻塞
time.sleep,用asyncio.sleep。 - 超时:
@background(schedule=5, timeout=30),超 30s 杀进程。
任务队列持久化与参数
任务持久化确保重启不丢。调用mytask('arg1', kw={'key':val}, schedule=60, repeat=5, result=True):
schedule=60:60s 后执行。repeat=5:成功后重复 5 次,每次 + schedule。result=True:执行结果存result字段,可Task.objects.get(pk=id).result查询。
多参数 JSON 序列化自动处理。证据:表CompletedTask存历史,便于审计。
配置参数:
| 参数 | 默认 | 描述 | 推荐 |
|---|---|---|---|
| schedule | 3s | 首次延迟 | 10-60s |
| max_attempts | 5 | 重试上限 | 10,高可靠 |
| lock_seconds | 300s | 锁时长 | 任务预计 * 2 |
| workers | 1 | process_tasks --workers=N | CPU 核数 |
分布式调度与重试策略
分布式天然:多机跑process_tasks --workers=4,统一 DB 共享队列。锁机制防双执行:执行中locked_by非空跳过。
重试:失败attempts +=1,若 < max,schedule = now() + min(300, schedule*2)。指数退避防雪崩。自定义:
@background(schedule=10, max_attempts=10)
def retry_task():
if fail: raise ValueError
回滚:Task.objects.filter(status='FAILED').delete()清理。
监控:admin 集成TaskAdmin,或 Prometheus scrape 表count(*) by status。
工程实践与风险阈值
生产部署:
- Supervisor/PM2 守护
process_tasks --duration=300 --sleep=5(每轮 300s,休眠 5s)。 - DB 索引
schedule, status。 - 规模 > 1k 任务 /d,迁 Celery。
风险阈值:
- 队列积压 > 1000:加 worker 或优化任务。
- DB 负载 > 20%:分表或 PostgreSQL 分区。
- 失败率 > 5%:日志告警,重试上限。
实际案例:电商订单异步发券,峰值 500/s,4worker 延迟 < 10s。
资料来源:django-background-tasks 文档、HN 讨论(roam.be 帖子)。
(字数:1256)