Hotdry.
application-security

剖析Django原生背景任务API:asyncio集成、任务队列持久化、分布式调度与重试策略

基于数据库的任务队列,无需Celery,支持异步任务定义、指数退避重试、多worker分布式执行与落地参数配置。

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

工程实践与风险阈值

生产部署:

  1. Supervisor/PM2 守护process_tasks --duration=300 --sleep=5(每轮 300s,休眠 5s)。
  2. DB 索引schedule, status
  3. 规模 > 1k 任务 /d,迁 Celery。

风险阈值:

  • 队列积压 > 1000:加 worker 或优化任务。
  • DB 负载 > 20%:分表或 PostgreSQL 分区。
  • 失败率 > 5%:日志告警,重试上限。

实际案例:电商订单异步发券,峰值 500/s,4worker 延迟 < 10s。

资料来源:django-background-tasks 文档、HN 讨论(roam.be 帖子)。

(字数:1256)

查看归档