Communitygithub.com

Django + Celery 异步任务模式

Django + Celery 异步任务模式 — 配置、任务设计、Beat调度、重试、Canvas工作流、监控与测试。在 Django 应用中添加后台作业、定时任务或异步处理时使用。

Django + Celery 异步任务模式 是什么?

该技能提供了在 Django 应用中使用 Celery 进行后台任务处理的生产级模式。它涵盖了从项目设置(包括 broker 选择、Celery 应用入口点以及 Django 设置配置)到高级任务设计模式(如基本任务、可重试任务、幂等性任务和软超时处理)的完整指南。技能详细说明了如何使用 `delay()`、`apply_async()` 进行任务调用,以及如何通过代码或数据库驱动的 CrontabSchedule 设置 Celery Beat 实现定时调度。此外,还介绍了 Canvas 工作流(链、组、和弦)以支持复杂任务编排,以及错误处理策略,包括死信队列和 Sentry 集成。测试部分涵盖了单元测试(无 broker)和集成测试(使用 CELERY_TASK_ALWAYS_EAGER),并提供了生产环境检查清单和反模式警示。该技能支持 Claude Code、Cursor、Codex 等所有主流 AI 智能体平台。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/django-celery

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Django + Celery 异步任务模式 是做什么的?

使用 Celery 配合 Redis 或 RabbitMQ 在 Django 中进行生产级后台任务处理。

何时激活

  • 为 Django 应用添加后台作业或异步处理
  • 实现周期性/定时任务
  • 将耗时操作(邮件、PDF 生成、API 调用)从请求周期中卸载
  • 设置 Celery Beat 实现类似 cron 的调度
  • 调试任务失败、重试或队列积压
  • 为 Celery 任务编写测试

项目设置

安装

pip install 'celery[redis]' django-celery-results django-celery-beat

celery.py — 应用入口点

# config/celery.py
import os
from celery import Celery

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.development')

app = Celery('myproject')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()  # 在每个 INSTALLED_APP 中发现 tasks.py

@app.task(bind=True, ignore_result=True)
def debug_task(self):
    print(f'Request: {self.request!r}')
# config/__init__.py
from .celery import app as celery_app

__all__ = ('celery_app',)

Django 设置

# config/settings/base.py

# Broker(生产环境推荐 Redis)
CELERY_BROKER_URL = env('CELERY_BROKER_URL', default='redis://localhost:6379/0')
CELERY_RESULT_BACKEND = env('CELERY_RESULT_BACKEND', default='django-db')

# 序列化
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'

# 任务行为
CELERY_TASK_TRACK_STARTED = True
CELERY_TASK_TIME_LIMIT = 30 * 60        # 硬限制:30 分钟
CELERY_TASK_SOFT_TIME_LIMIT = 25 * 60   # 软限制:发送 SoftTimeLimitExceeded
CELERY_WORKER_PREFETCH_MULTIPLIER = 1   # 防止 worker 囤积长时间任务
CELERY_TASK_ACKS_LATE = True            # 当 worker 崩溃时重新入队

# 结果持久化
CELERY_RESULT_EXPIRES = 60 * 60 * 24   # 结果保留 24 小时

# Beat 调度器(用于周期性任务)
CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler'

# 已安装的应用
INSTALLED_APPS += [
    'django_celery_results',
    'django_celery_beat',
]

运行 Worker

# 启动 worker(开发环境)
celery -A config worker --loglevel=info

# 启动 Beat 调度器(周期性任务)
celery -A config beat --loglevel=info --scheduler django_celery_beat.schedulers:DatabaseScheduler

# 整合 worker + beat(仅开发环境,绝不用在生产环境)
celery -A config worker --beat --loglevel=info

# 生产环境:多个 worker 并发执行
celery -A config worker --loglevel=warning --concurrency=4 -Q default,high_priority

任务设计模式

基本任务

# apps/notifications/tasks.py
from celery import shared_task
import logging

logger = logging.getLogger(__name__)

@shared_task(name='notifications.send_welcome_email')
def send_welcome_email(user_id: int) -> None:
    """向新注册用户发送欢迎邮件。"""
    from apps.users.models import User
    from apps.notifications.services import EmailService

    try:
        user = User.objects.get(pk=user_id)
    except User.DoesNotExist:
        logger.warning('send_welcome_email: user %s not found', user_id)
        return  # 幂等——不抛出,任务已不可能完成

    EmailService.send_welcome(user)
    logger.info('Welcome email sent to user %s', user_id)

可重试任务

@shared_task(
    bind=True,
    name='integrations.sync_to_crm',
    max_retries=5,
    default_retry_delay=60,       # 首次重试前的秒数
    autoretry_for=(ConnectionError, TimeoutError),
    retry_backoff=True,           # 指数退避
    retry_backoff_max=600,        # 上限为 10 分钟
    retry_jitter=True,            # 随机化以避免惊群效应
)
def sync_contact_to_crm(self, contact_id: int) -> dict:
    """将联系人同步到外部 CRM,并在瞬时失败时重试。"""
    from apps.crm.services import CRMClient

    try:
        result = CRMClient().sync(contact_id)
        return result
    except CRMClient.RateLimitError as exc:
        # 从响应头获取特定重试延迟
        raise self.retry(exc=exc, countdown=int(exc.retry_after))

幂等任务模式

设计任务使其在相同输入下能安全执行多次:

@shared_task(name='orders.mark_shipped')
def mark_order_shipped(order_id: int, tracking_number: str) -> None:
    """将订单标记为已发货——可安全执行多次。"""
    from apps.orders.models import Order

    updated = Order.objects.filter(
        pk=order_id,
        status=Order.Status.PROCESSING,    # 防护:只有未发货时才更新
    ).update(
        status=Order.Status.SHIPPED,
        tracking_number=tracking_number,
    )

    if not updated:
        logger.info('mark_order_shipped: order %s already shipped or not found', order_id)

带软超时的任务

from celery.exceptions import SoftTimeLimitExceeded

@shared_task(
    bind=True,
    name='reports.generate_pdf',
    soft_time_limit=120,
    time_limit=150,
)
def generate_pdf_report(self, report_id: int) -> str:
    """生成 PDF 报告,并优雅处理超时。"""
    from apps.reports.services import PDFGenerator

    try:
        path = PDFGenerator.build(report_id)
        return path
    except SoftTimeLimitExceeded:
        # 在硬杀死前清理部分文件
        PDFGenerator.cleanup(report_id)
        raise

调用任务

from datetime import timedelta
from django.utils import timezone

# 发起并遗忘(异步)
send_welcome_email.delay(user.pk)

# 安排到将来执行
send_reminder.apply_async(args=[user.pk], countdown=3600)  # 1 小时后
send_reminder.apply_async(args=[user.pk], eta=timezone.now() + timedelta(days=1))

# 使用队列路由执行
sync_contact_to_crm.apply_async(args=[contact.pk], queue='high_priority')

# 同步执行(仅测试/调试)
result = generate_pdf_report.apply(args=[report.pk])

Beat 调度(周期性任务)

代码定义的调度

# config/settings/base.py
from celery.schedules import crontab

CELERY_BEAT_SCHEDULE = {
    'cleanup-expired-sessions': {
        'task': 'users.cleanup_expired_sessions',
        'schedule': crontab(hour=2, minute=0),   # 每天凌晨 2 点
    },
    'sync-inventory': {
        'task': 'products.sync_inventory',
        'schedule': 60.0,                         # 每 60 秒
    },
    'weekly-digest': {
        'task': 'notifications.send_weekly_digest',
        'schedule': crontab(day_of_week='monday', hour=8, minute=0),
    },
}

数据库定义的调度(通过 django-celery-beat)

# 从 Django 管理后台或代码管理周期性任务
from django_celery_beat.models import PeriodicTask, CrontabSchedule
import json

schedule, _ = CrontabSchedule.objects.get_or_create(
    hour='*/6', minute='0',
    timezone='UTC',
)

PeriodicTask.objects.update_or_create(
    name='每 6 小时同步库存',
    defaults={
        'crontab': schedule,
        'task': 'products.sync_inventory',
        'args': json.dumps([]),
        'enabled': True,
    }
)

Canvas:任务链与分组

from celery import chain, group, chord

# 链:依次执行任务,传递结果
pipeline = chain(
    fetch_data.s(source_id),
    transform_data.s(),          # 接收 fetch_data 的结果作为第一个参数
    load_to_warehouse.s(),
)
pipeline.delay()

# 分组:并行执行任务
parallel = group(
    send_welcome_email.s(user_id)
    for user_id in new_user_ids
)
parallel.delay()

# 和弦:并行任务 + 全部完成后回调
result = chord(
    group(process_chunk.s(chunk) for chunk in data_chunks),
    aggregate_results.s(),       # 接收所有分块结果的列表
)
result.delay()

错误处理与死信队列

# apps/core/tasks.py
from celery.signals import task_failure

@task_failure.connect
def on_task_failure(sender, task_id, exception, args, kwargs, traceback, einfo, **kw):
    """将所有任务失败记录到 Sentry 或告警系统。"""
    import sentry_sdk
    with sentry_sdk.new_scope() as scope:
        scope.set_context('celery', {
            'task': sender.name,
            'task_id': task_id,
            'args': args,
            'kwargs': kwargs,
        })
        sentry_sdk.capture_exception(exception)
# 达到最大重试次数后,将失败任务路由到死信队列
@shared_task(
    bind=True,
    max_retries=3,
    name='payments.charge_card',
)
def charge_card(self, order_id: int) -> None:
    from apps.payments.models import Order, FailedCharge

    try:
        _do_charge(order_id)
    except Exception as exc:
        if self.request.retries >= self.max_retries:
            # 持久化到死信表以待人工审查
            FailedCharge.objects.create(
                order_id=order_id,
                error=str(exc),
                task_id=self.request.id,
            )
            return  # 不抛出——任务已永久失败
        raise self.retry(exc=exc)

测试 Celery 任务

单元测试(无 Broker)

# tests/test_tasks.py
import pytest
from unittest.mock import patch, MagicMock
from apps.notifications.tasks import send_welcome_email

class TestSendWelcomeEmail:

    @pytest.mark.django_db
    def test_sends_email_to_existing_user(self, user):
        with patch('apps.notifications.services.EmailService') as mock_email:
            send_welcome_email(user.pk)
            mock_email.send_welcome.assert_called_once_with(user)

    @pytest.mark.django_db
    def test_skips_missing_user_gracefully(self):
        """用户入队后执行前被删除,不应引发异常。"""
        send_welcome_email(99999)  # 不存在的用户——不得抛出异常

使用 CELERY_TASK_ALWAYS_EAGER 的集成测试

# config/settings/test.py
CELERY_TASK_ALWAYS_EAGER = True      # 测试中同步执行任务
CELERY_TASK_EAGER_PROPAGATES = True  # 重新引发任务的异常

# tests/test_integration.py
@pytest.mark.django_db
def test_registration_triggers_welcome_email(client):
    with patch('apps.notifications.services.EmailService') as mock_email:
        response = client.post('/api/users/', {
            'email': '[email protected]',
            'password': 'strongpass123',
        })

    assert response.status_code == 201
    mock_email.send_welcome.assert_called_once()

测试重试功能

@pytest.mark.django_db
def test_task_retries_on_connection_error():
    with patch('apps.crm.services.CRMClient.sync') as mock_sync:
        mock_sync.side_effect = ConnectionError('timeout')

        with pytest.raises(ConnectionError):
            sync_contact_to_crm.apply(args=[1], throw=True)

        assert mock_sync.call_count == 1  # eager 模式下仅首次尝试

监控

# 检查活动的 worker 和队列
celery -A config inspect active
celery -A config inspect stats
celery -A config inspect reserved

# 检查队列长度(Redis)
redis-cli llen celery

# Flower:基于 Web 的实时监控
pip install flower
celery -A config flower --port=5555

反模式

# 错误:传递模型实例——执行时可能已过时
send_welcome_email.delay(user)        # 绝不传递 ORM 对象
send_welcome_email.delay(user.pk)     # 始终传递 PK

# 错误:在生产视图中同步调用任务
result = generate_report.apply()      # 阻塞请求线程

# 错误:没有防护的非幂等任务
@shared_task
def charge_and_fulfill(order_id):
    order.charge()     # 如果任务重试,可能重复扣款!
    order.fulfill()

# 正确:使用状态防护实现幂等
@shared_task
def charge_and_fulfill(order_id):
    order = Order.objects.select_for_update().get(pk=order_id)
    if order.status != Order.Status.PENDING:
        return  # 已处理
    order.charge()
    order.fulfill()

生产环境检查清单

检查项设置
Worker 崩溃后自动重启supervisord 或 systemd 单元
CELERY_TASK_ACKS_LATE = TrueWorker 崩溃时任务重新入队
CELERY_WORKER_PREFETCH_MULTIPLIER = 1长时间任务公平分发
按优先级划分独立队列-Q default,high_priority,low_priority
设置 CELERY_TASK_SOFT_TIME_LIMIT硬杀死前优雅超时
Sentry 集成捕获所有 task_failure 信号
Flower 或其他监控队列深度可视化
Beat 仅在单一节点上运行防止定时任务重复执行

相关技能

  • django-patterns — ORM、服务层和项目结构
  • django-tdd — 测试 Django 模型、视图和服务
  • python-testing — pytest 配置和 Fixtures

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

相关技能