Communitygithub.com

Django-Celery 非同步任務模式

Django + Celery 非同步任務模式 — 包含配置、任務設計、排程、重試、Canvas 工作流、監控與測試。適用於在 Django 應用中加入背景任務、排程或非同步處理。

Django-Celery 非同步任務模式 是什麼?

Django-Celery 非同步任務模式技能提供了一套生產級的指南,協助開發者在 Django 應用程式中使用 Celery 搭配 Redis 或 RabbitMQ 來處理背景任務。此技能涵蓋了完整的專案設置,包括 Celery 應用程式的初始化、Django 設定(如代理、序列化、任務行為限制),以及如何啟動 Worker 和 Beat 排程程式。在任務設計上,技能詳細說明了基本任務、可重試任務(含指數退避和抖動)、冪等任務模式以及軟時間限制的處理,確保任務執行穩固且不重複。此外,還說明了多種呼叫任務的方式、使用 Crontab 和資料庫進行週期性任務排程、利用 Chain、Group 和 Chord 進行複雜的工作流程編排、錯誤處理與死信佇列機制,以及完整的測試方法(單元測試與整合測試)和監控工具(如 Flower)。技能最後還提供了共同反模式和生產環境檢查清單,幫助開發者避免常見陷阱,適用於 Cursor、Claude Code 和 Codex 等開發助理平台。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/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: 找不到用戶 %s', user_id)
        return  # 冪等 — 不要拋出例外,任務已無法完成

    EmailService.send_welcome(user)
    logger.info('歡迎郵件已發送給用戶 %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: 訂單 %s 已發貨或找不到', 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

# Chain:依序執行任務,傳遞結果
pipeline = chain(
    fetch_data.s(source_id),
    transform_data.s(),          # 接收 fetch_data 的結果作為第一個參數
    load_to_warehouse.s(),
)
pipeline.delay()

# Group:並行執行任務
parallel = group(
    send_welcome_email.s(user_id)
    for user_id in new_user_ids
)
parallel.delay()

# Chord:並行任務 + 全部完成後的回呼
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:網頁即時監控
pip install flower
celery -A config flower --port=5555

反模式

# 錯誤:傳遞模型實例 — 執行時可能已過時
send_welcome_email.delay(user)        # 絕不傳遞 ORM 物件
send_welcome_email.delay(user.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/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

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.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

相關技能