التعامل مع Background Tasks في Django وFastAPI

التعامل مع Background Tasks في Django وFastAPI باستخدام Celery وRQ وFastAPI BackgroundTasks

في كثير من تطبيقات الويب نحتاج لتنفيذ مهام تأخذ وقتًا طويلاً دون أن نُبطئ استجابة الـ API أو واجهة المستخدم، مثل إرسال رسائل بريدية، معالجة صور، توليد تقارير، أو استدعاء خدمات خارجية. هنا يأتي دور Background tasks مع أطر مثل Django وFastAPI.

في هذا المقال على افهم صح سنشرح كيفية التعامل مع Background tasks Django FastAPI باستخدام:

  • Celery مع Django وFastAPI
  • RQ (Redis Queue)
  • FastAPI BackgroundTasks

كما سنقارن بين هذه الحلول، ومتى تختار كل واحد منها، مع ربط الموضوع بمفاهيم مثل البرمجة غير المتزامنة والـ APIs. إذا لم تكن معتادًا على FastAPI يمكنك الرجوع إلى مقالنا بناء RESTful APIs باستخدام FastAPI، ولتعمّق أكبر في مفهوم اللا تزامن في بايثون راجع البرمجة غير المتزامنة في بايثون.

لماذا نحتاج Background Tasks أصلًا؟

في التطبيقات التقليدية (Synchronous)، كل Request يُنفّذ في Thread أو Process واحد، وأي عملية ثقيلة (مثل إرسال 1000 إيميل) ستُبطئ الاستجابة أو تؤدي لانتهاء المهلة (Timeout).

بدلاً من تنفيذ هذه العمليات داخل نفس طلب الـ HTTP، نقوم بـ:

  1. استقبال الطلب من المستخدم.
  2. دفع مهمة (Task) إلى طابور (Queue) مثل Redis أو RabbitMQ.
  3. إرجاع استجابة سريعة للمستخدم (مثل: "تم استلام طلبك").
  4. تنفيذ المهمة في الخلفية عن طريق Worker منفصل.

هذا النمط أساسي لتطبيقات عالية الأداء، خاصة مع Django وFastAPI، ويكمّل ما تتعلّمه في موضوعات مثل Threading في بايثون والبرمجة غير المتزامنة.

مفاهيم أساسية: Task Queue وWorker وBroker

  • Task Queue: نظام لطابور المهام، يحتفظ بمهام تنتظر أن تُنفّذ.
  • Broker: الوسيط/الخادم الذي يُخزّن الطابور (مثل Redis، RabbitMQ).
  • Worker: عملية (Process) تعمل في الخلفية وتقوم بسحب المهام من الطابور وتنفيذها.

الحلول الأكثر شهرة لبيثون في هذا المجال:

  • Celery: الأشهر والأقوى، يدعم جدولة المهام، Retries، Routing، Monitoring.
  • RQ (Redis Queue): أبسط وأخف، يعتمد بالكامل على Redis.
  • FastAPI BackgroundTasks: حل مدمج في FastAPI للمهام البسيطة داخل نفس السيرفر.

Background Tasks في Django باستخدام Celery

لماذا Celery مع Django؟

Django بنفسه لا يوفّر نظام Background Tasks متكامل، لكنه يتكامل بسهولة مع Celery. إذا كنت تبني نظامًا كبيرًا (E-commerce، SaaS) يحتاج لمهام معقدة (سلاسل Tasks، جدولة، Retries)، فـ Celery غالبًا هو الخيار الأول.

إعداد Celery في مشروع Django

نفترض أن لديك مشروع Django جاهز. إن لم يكن لديك خبرة كافية مع Django راجع الدليل الشامل حول إطار Django.

1. تثبيت الحزم اللازمة

pip install celery[redis] redis

هنا نستعمل Redis كـ Broker (يمكنك استخدام RabbitMQ أو غيره).

2. إنشاء ملف celery.py في مشروع Django

في نفس مجلد settings.py (غالبًا مجلد المشروع الرئيسي):

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

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project_name.settings")

app = Celery("project_name")

# قراءة الإعدادات من Django واستخدام namespace للـ CELERY_*
app.config_from_object("django.conf:settings", namespace="CELERY")

# اكتشاف المهام تلقائيًا من apps المسجلة
app.autodiscover_tasks()

وفي ملف __init__.py لنفس مجلد المشروع:

# project_name/__init__.py
from .celery import app as celery_app

__all__ = ("celery_app",)

3. إعداد Broker في إعدادات Django

# settings.py
CELERY_BROKER_URL = "redis://localhost:6379/0"
CELERY_RESULT_BACKEND = "redis://localhost:6379/1"

4. تعريف Task في أحد تطبيقات Django

في أي app داخل المشروع، أنشئ ملف tasks.py:

# app_name/tasks.py
from celery import shared_task
from django.core.mail import send_mail

@shared_task
def send_welcome_email(user_email):
    send_mail(
        subject="مرحبًا بك",
        message="شكرًا لتسجيلك في موقعنا.",
        from_email="[email protected]",
        recipient_list=[user_email],
        fail_silently=False,
    )

5. استدعاء الـ Task من Django View

يمكنك استدعاء المهمة في الخلفية بدلًا من تنفيذها مباشرة:

# app_name/views.py
from django.http import JsonResponse
from .tasks import send_welcome_email

def register_user(request):
    # من المفترض هنا إنشاء المستخدم وحفظه في قاعدة البيانات
    user_email = "[email protected]"

    # استدعاء المهمة في الخلفية
    send_welcome_email.delay(user_email)

    return JsonResponse({"detail": "تم التسجيل وسيصلك بريد ترحيبي قريبًا"})

6. تشغيل Worker الخاص بـ Celery

celery -A project_name worker -l info

يمكنك أيضًا تشغيل Beat للمهام المجدولة، لكن هذا خارج نطاق هذه المقدمة.

مميزات Celery في Django

  • مناسب لتطبيقات الإنتاج الضخمة.
  • يدعم Retries، Schedule، Chains، Groups.
  • تكامل قوي مع Django (استخدام ORM داخل Tasks مع الحذر من الأداء).

عيوب Celery

  • إعداداته معقدة نسبيًا مقارنةً بـ RQ.
  • يحتاج لموارد وتشغيل Services إضافية (Redis/RabbitMQ + Workers).

Background Tasks في Django باستخدام RQ (Redis Queue)

لماذا RQ؟

إذا كنت تحتاج لحل أبسط من Celery، وتستخدم Redis بالفعل، فـ RQ خيار ممتاز. يناسب المشاريع الصغيرة والمتوسطة، أو الحالات التي لا تحتاج كل خصائص Celery.

تثبيت وإعداد RQ

pip install rq redis django-rq

1. إعداد Django RQ في settings.py

INSTALLED_APPS = [
    # ...
    "django_rq",
]

RQ_QUEUES = {
    "default": {
        "HOST": "localhost",
        "PORT": 6379,
        "DB": 0,
        "DEFAULT_TIMEOUT": 360,
    }
}

2. إضافة URLs لـ Django RQ Dashboard (اختياريًا)

# urls.py
from django.urls import path, include

urlpatterns = [
    # ...
    path("django-rq/", include("django_rq.urls")),
]

3. تعريف Task واستخدام RQ

# app_name/tasks.py
import time

def long_running_task(user_id):
    # مثال لمهمة تستغرق وقتًا
    time.sleep(10)
    print(f"تم تنفيذ المهمة للمستخدم {user_id}")

استدعاء المهمة من View:

# app_name/views.py
import django_rq
from django.http import JsonResponse
from .tasks import long_running_task

def trigger_task(request):
    queue = django_rq.get_queue("default")
    job = queue.enqueue(long_running_task, user_id=123)

    return JsonResponse({"job_id": job.id, "status": "queued"})

4. تشغيل Worker لـ RQ

python -m rq worker default

مميزات RQ مع Django

  • بسيط في الإعداد والاستخدام.
  • يعتمد فقط على Redis (بدون تعقيدات إضافية).
  • يدعم Dashboard بسيطة لمتابعة المهام.

عيوب RQ

  • خصائص أقل من Celery (خاصةً في جدولة المهام المعقدة).
  • لا يناسب جداً السيناريوهات شديدة التعقيد أو مع مئات الآلاف من المهام المتكررة.

Background Tasks في FastAPI باستخدام BackgroundTasks

الحل المدمج في FastAPI

FastAPI يوفر كلاس جاهز اسمه BackgroundTasks لتنفيذ مهام خفيفة في الخلفية بعد إرجاع الاستجابة. هذا الحل لا يعتمد على Redis أو RabbitMQ؛ المهام تُنفذ داخل نفس عملية السيرفر.

هذا مناسب لـ:

  • مهام بسيطة وسريعة نسبيًا.
  • تطبيقات صغيرة أو داخل بيئة محدودة.
  • عمليات لا تحتاج لضمان التنفيذ في حال إعادة تشغيل السيرفر.

مثال بسيط باستخدام FastAPI BackgroundTasks

from fastapi import FastAPI, BackgroundTasks

app = FastAPI()

def send_email(email: str, message: str):
    # من المفترض هنا تنفيذ منطق الإرسال الفعلي
    print(f"إرسال بريد إلى {email}: {message}")

@app.post("/register")
def register_user(email: str, background_tasks: BackgroundTasks):
    # منطق التسجيل...

    # إضافة مهمة للإرسال في الخلفية
    background_tasks.add_task(send_email, email, "مرحبًا بك في موقعنا!")

    return {"detail": "تم التسجيل، وسيتم إرسال رسالة ترحيبية في الخلفية"}

ما الذي يحدث فعليًا؟

بمجرد إرجاع الاستجابة، يقوم FastAPI بتشغيل الوظائف المُضافة إلى background_tasks في Thread منفصل (أو Event Loop) لكن داخل نفس عملية التطبيق. لا يوجد Broker خارجي أو Worker مستقل.

متى لا يكون BackgroundTasks كافيًا؟

  • إذا كانت المهمة طويلة جدًا (ثواني/دقائق) وتستهلك موارد كبيرة.
  • إذا أردت أن تستمر المهام حتى لو تم إعادة تشغيل السيرفر أو فشل Container.
  • إذا أردت استخدام جدولة أو Retries أو متابعة حالة المهام.

في هذه الحالات ستحتاج لحلول مثل Celery أو RQ مع FastAPI أيضًا، وعندها يكون تعلم الدوكر مفيدًا جدًا لتشغيل Workers وBrokers داخل حاويات منفصلة.

دمج Celery أو RQ مع FastAPI

مثلما فعلنا مع Django، يمكن أيضًا استخدام Celery أو RQ مع FastAPI. الفكرة العامة:

  1. تُعدّ Celery/RQ في ملف مستقل (مثلاً celery_app.py).
  2. تُعرِّف Tasks داخل هذا الملف أو داخل Modules أخرى.
  3. من داخل FastAPI endpoint تستدعي task.delay(...) (Celery) أو queue.enqueue(...) (RQ).

مثال مختصر: استخدام Celery مع FastAPI

# celery_app.py
from celery import Celery

celery_app = Celery(
    "fastapi_app",
    broker="redis://localhost:6379/0",
    backend="redis://localhost:6379/1",
)

@celery_app.task
def process_video(video_id: int):
    # منطق معالجة فيديو ثقيل
    return {"status": "done", "video_id": video_id}

في ملف FastAPI الرئيسي:

# main.py
from fastapi import FastAPI
from celery_app import process_video

app = FastAPI()

@app.post("/videos/{video_id}/process")
def process_video_endpoint(video_id: int):
    job = process_video.delay(video_id)
    return {"task_id": job.id, "status": "queued"}

بهذا الشكل تحصل على أفضل ما في FastAPI (سرعة الـ API) مع قوة Celery في الـ Background Tasks.

المقارنة بين Celery وRQ وFastAPI BackgroundTasks

1. البنية المعمارية (Architecture)

  • Celery: يحتاج Broker (Redis/RabbitMQ) وWorkers مستقلين؛ قوي ومرن.
  • RQ: يعتمد على Redis فقط؛ أبسط من Celery.
  • FastAPI BackgroundTasks: لا يحتاج أي خدمة خارجية؛ كل شيء داخل نفس السيرفر.

2. قوة الميزات

  • Celery:
    • Retries آلية عند الفشل.
    • جدولة (Periodic Tasks) مع Celery Beat.
    • Chords, Chains, Groups للمهام المركبة.
    • Monitoring عبر أدوات مثل Flower.
  • RQ:
    • خصائص أساسية: Queue، Delayed Jobs، Retry بسيط.
    • Dashboard بسيطة عبر django-rq أو rq-dashboard.
  • BackgroundTasks:
    • ينفّذ وظائف بسيطة بعد الاستجابة فقط.
    • بدون Retry، بدون جدولة، بدون Persistence حقيقي.

3. حالات الاستخدام النموذجية

  • Celery:
    • أنظمة كبيرة تحتاج لإرسال مئات الآلاف من الرسائل.
    • معالجة كبيرة للصور/الفيديو/الملفات.
    • تطبيقات تحتاج لتسلسل مهام معقّد وجدولة دورية.
  • RQ:
    • تطبيقات متوسطة الحجم.
    • فريق صغير يريد حلاً بسيطًا وسهل الصيانة.
    • مشاريع Django تستخدم Redis بالفعل وتريد Queue خفيف.
  • FastAPI BackgroundTasks:
    • APIs صغيرة/متوسطة بأعباء خفيفة.
    • مهام مثل كتابة Log أو إشعار بسيط بعد الاستجابة.
    • Proof of Concept أو MVP لا يحتاج بنية معقدة.

4. الأداء وقابلية التوسع (Scalability)

  • Celery: الأعلى في القابلية للتوسع، يمكن توزيع الـ Workers على عدة خوادم.
  • RQ: جيّد جدًا للتوسع مع Redis، لكن يحتاج تنظيم يدوي أكثر من Celery.
  • BackgroundTasks: محدود بتعداد عمليات/Threads سيرفر التطبيق نفسه؛ عند زيادة الحمل ستحتاج للانتقال لحلول طابور حقيقية.

أفضل الممارسات عند بناء Background Tasks

  • لا تنفّذ مهام ثقيلة في نفس Request/Response Cycle، خاصة في Django (Synchronous) أو في Endpoints FastAPI الحرجة.
  • احفظ فقط المعرفات (IDs) في المهام بدلاً من تمرير كائنات كبيرة أو بيانات حساسة، ثم استرجع البيانات من قاعدة البيانات داخل الـ Task.
  • تعامل مع الأخطاء (Exception Handling) داخل المهام وسجّل (Logging) أي فشل.
  • راقب موارد النظام عند تشغيل Workers عديدة (CPU, RAM, I/O)، خاصة في بيئات Docker/Kubernetes.
  • استفد من البرمجة غير المتزامنة خاصة مع FastAPI؛ في بعض الحالات قد يغنيك async/await عن Task Queue كامل إذا كانت العمليات I/O Bound وليست CPU Bound.

الخلاصة: كيف تختار بين Celery وRQ وBackgroundTasks؟

  • اختر FastAPI BackgroundTasks إذا:
    • تطبيقك بسيط ولا يحتاج لـ Queue منفصل.
    • المهام خفيفة ولن تتسبب في بطء ملحوظ.
    • لا تحتاج للتعامل مع فشل السيرفر أو جدولة زمنية.
  • اختر RQ إذا:
    • تستخدم Redis بالفعل وتحتاج لحل خفيف وسهل مع Django.
    • تريد Queue حقيقية لكن بدون تعقيد Celery.
  • اختر Celery إذا:
    • تتعامل مع حجم مهام كبير أو منطق معقّد.
    • تحتاج Retries وSchedule وMonitoring متقدم.
    • تريد حلاً مجرَّبًا على نطاق واسع في الإنتاج.

فهم Background tasks Django FastAPI خطوة أساسية لتصميم تطبيقات ويب قوية وقابلة للتوسع. مع اختيار الأداة المناسبة (Celery، RQ، أو FastAPI BackgroundTasks) ستتمكن من عزل المهام الثقيلة عن استجابة المستخدم، وتحسين الأداء بشكل ملحوظ.

لإكمال الصورة حول بناء APIs عالية الأداء، يمكنك الاطلاع أيضًا على:

حول المحتوى:

شرح كيفية تنفيذ المهام الخلفية باستخدام Celery وRQ وFastAPI BackgroundTasks، ومقارنة بين هذه الحلول.

هل كان هذا مفيدًا لك؟

أضف تعليقك