توی دنیای درخواست و پاسخ (Request/Response) جنگو، زندگی خیلی خطی و اتو کشیده هست. کاربر روی دکمه کلیک میکنه، جنگو درخواست رو میگیره، پردازش میکنه، دیتابیس رو شخم میزنه و جواب رو پس میده. همه چیز عالیه... تا وقتی که درخواست کاربر "سنگین" باشه.

تصور کنید رفتید رستوران و گارسون سفارش شما رو میگیره. اما به جای اینکه سفارش رو بده به آشپزخانه و بره سراغ مشتری بعدی، خودش میره توی آشپزخانه، پیشبند میبنده، شروع میکنه به پوست کندن پیاز، کباب کردن گوشت و شستن ظرف‌ ها! شما و بقیه مشتری‌ ها باید منتظر بمونید تا  کار جناب گارسون تموم بشه. این فاجعه است، نه؟ جنگو به صورت پیش‌ فرض (Synchronous) دقیقا همین گارسون "همه کاره اما کند" هست. اگر بخواید ایمیل خوش ‌آمد گویی بفرستید یا یک گزارش PDF سنگین بسازید، مرورگر کاربر روی حالت Loading قفل میکنه و کاربر به دیوار خیره میشه.

اینجاست که Celery مثل یک ناجی وارد میشه. ابزاری که میگه: "هی جنگو! تو فقط سفارش رو بگیر و لبخند بزن، کارهای سخت و زمانبر با من!"

چالش اصلی

بگذارید روراست باشیم. کاربر امروزی صبر ایوب نداره. آمار میگه اگر لود شدن صفحه بیشتر از ۳ ثانیه طول بکشه، کاربر تب رو میبنده و میره سراغ رقیب. حالا فرض کنید کاربر فرم ثبت نام رو پر کرده. شما باید: ۱. اطلاعات رو ذخیره کنید. ۲. یک ایمیل تایید بفرستید (که ممکنه ۳-۴ ثانیه طول بکشه چون سرور ایمیل کنده). ۳. عکس پروفایلش رو ریسایز کنید (پردازش تصویر CPU رو درگیر میکنه).

اگر همه اینها رو توی views.py بنویسید، عملاً دارید مرورگر کاربر رو گروگان میگیرید. کاربر فکر میکنه سایت شما خرابه یا اینترنتش قطع شده. راه حل چیه؟ Asynchronous Task Queue یا همون صف وظایف ناهمگام.

مثلث قدرت: Django + Redis + Celery

مثلث قدرت: Django + Redis + Celery

برای اینکه این معماری رو بفهمیم، باید با سه بازیگر اصلی آشنا بشیم. بیایید برگردیم به مثال رستوران:

  • جنگو (Producer): گارسون  فقط سفارش (Task) رو میگیره و خیلی سریع یه "تیکت" مینویسه.

  • ردیس (Broker/Redis): این همون "تخته‌ میخی" توی آشپزخانه است که گارسون تیکت‌ ها رو بهش میچسبونه. ردیس (یا RabbitMQ) وظیفه ‌اش اینه که پیام‌ ها رو نگه داره تا یکی بیاد سراغشون.

  • سلری (Consumer/Worker): این‌ها آشپز های ماهر توی آشپزخانه هستن. این‌ها مدام به تخته (Redis) نگاه میکنن، تیکت رو برمیدارن و در پس‌زمینه (Background) غذا رو میپزن، بدون اینکه مشتری (User) معطل بشه.

پیاده‌سازی صحیح

اگر میخوایم جنگو رو از کار های یدی خلاص کنیم، باید سلری رو بهش وصل کنیم. 

۱. نصب ابزارها (The Toolkit): اول باید سلری و ردیس رو نصب کنید (فرض بر اینه که سرور Redis رو روی سیستم دارید):

pip install celery redis

۲. تنظیمات اولیه (The Setup): باید به جنگو بگیم که یک همکار جدید داره. در پوشه اصلی پروژه (کنار settings.py)، یک فایل به نام celery.py بسازید:

# myproject/celery.py

import os
from celery import Celery

# تنظیمات محیطی جنگو رو ست میکنیم تا سلری بتونه مدل‌ها رو بشناسه
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')

app = Celery('myproject')

# تنظیمات رو از فایل settings.py میخونه (هر چیزی که اولش CELERY_ باشه)
app.config_from_object('django.conf:settings', namespace='CELERY')

# به صورت خودکار دنبال تسک‌ها توی اپ‌های نصب شده میگرده
app.autodiscover_tasks()

۳. تعریف تسک (The Recipe): حالا بیایید یک کار سنگین رو تعریف کنیم. مثلاً ارسال ایمیل. توی یکی از اپ‌ هاتون (مثلا accounts) یک فایل tasks.py بسازید:

# accounts/tasks.py
from celery import shared_task
import time

@shared_task
def send_welcome_email_task(user_id):
    # شبیه‌سازی یک کار سنگین
    print(f"Start sending email to user {user_id}...")
    time.sleep(5)  # فرض کنید این ۵ ثانیه طول میکشه!
    print("Email sent successfully!")
    return "Done"

۴. فراخوانی تسک (Fire and Forget): حالا توی views.py، به جای اینکه خودتون ایمیل بفرستید، کار رو میسپارید به سلری. نکته کلیدی استفاده از متد .delay() هست:

# accounts/views.py
from django.shortcuts import render
from django.contrib.auth.models import User
from .tasks import send_welcome_email_task

def register(request):
    # ... کد ثبت نام کاربر ...
    user = User.objects.create(username="ali")
    
    # اینجا جادو اتفاق میفته!
    # ما صبر نمیکنیم تابع اجرا شه، فقط میندازیمش توی صف و رد میشیم
    send_welcome_email_task.delay(user.id)
    
    return render(request, 'success.html')

نکته فنی: دقت کردید؟ من آبجکت user رو به تسک پاس ندادم، بلکه user.id رو دادم. قانون طلایی سلری: هیچوقت آبجکت ‌های پیچیده دیتابیس (Model Instances) رو به تسک پاس ندید! چون تا وقتی تسک اجرا بشه ممکنه دیتا عوض شده باشه. همیشه ID بفرستید و توی تسک دوباره دیتا رو از دیتابیس بگیرید.

چرا این روش معجزه میکنه؟

زمانی که شما .delay() رو صدا میزنید، جنگو فقط چند میلی‌ثانیه وقت میذاره تا پیام رو بفرسته به Redis و بلافاصله به کاربر پیام "ثبت نام موفق" رو نشون میده. کاربر حس میکنه سایت شما مثل جت سریعه! در حالی که ۵ ثانیه بعد، توی سرور، سلری داره عرق میریزه و ایمیل رو میفرسته.

جمع‌بندی

استفاده از Celery فقط برای "سریع ‌تر کردن" نیست، برای "مقیاس‌پذیر کردن" (Scalability) حیاتیه.

  • اگر فردا تعداد ایمیل‌ هاتون زیاد شد، لازم نیست سرور اصلی جنگو رو ارتقا بدید. کافیه چند تا Worker جدید (آشپز جدید) به سیستم اضافه کنید تا صف Redis رو سریع‌تر خالی کنن.

  • جنگو  رو بزارید برای مدیریت منطق و دیتابیس (مغز متفکر).

  • سلری رو بزارید برای کار های یدی و زمانبر (بازوی اجرایی).

هنر یک توسعه‌دهنده Backend اینه که بدونه کی باید خودش ظرف ‌ها رو بشوره و کی باید کار رو بسپاره به ماشین ظرفشویی! استفاده از Celery یعنی ارتقاء رستوران شما از یک اغذیه ‌فروشی تک ‌نفره به یک رستوران حرفه ‌ای و مکانیزه.