Communitygithub.com

django-celery

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

django-celery 是什麼?

這項技能提供了一套完整的 production-grade 模式,用於在 Django 應用程式中使用 Celery 搭配 Redis 或 RabbitMQ 進行背景任務處理。它涵蓋了從專案初始化(包括 celery.py 入口點、Django 設定和 worker 啟動)到進階任務設計模式(如基本任務、可重試任務、冪等任務和軟時間限制任務)的所有環節。技能也深入說明了如何呼叫任務、使用 Celery Beat 進行基於程式碼或資料庫的定時排程、以及利用 Canvas 進行任務鏈(chain)、群組(group)和合弦(chord)等工作流程組合。此外,還包含錯誤處理與死信佇列實作、單元測試與整合測試策略(包括 CELERY_TASK_ALWAYS_EAGER 模式),以及使用 Flower 等工具的監控方法。最後提供了一個生產環境檢查清單,並指出了常見的反模式,以確保任務系統的穩定性與可維護性。

相容平台~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 非同步任務模式

在 Django 中使用 Celery 搭配 Redis 或 RabbitMQ 進行背景任務處理的生產級模式。

何時啟用

  • 為 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 配置和測試固件

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.

相關技能