حول المحتوى:
شرح ربط Django مع RabbitMQ لإرسال الإشعارات في الخلفية وتحسين الأداء.
إذا كنت تبني مشروع Django فيه إرسال إشعارات (Emails، SMS، Push Notifications، إشعارات داخل النظام)، فغالبًا ستواجه مشكلة في الأداء عند تنفيذ هذه المهام مباشرة داخل request/response. هنا يأتي دور RabbitMQ كـ Message Broker يساعدك على تنفيذ إرسال الإشعار في الخلفية بشكل غير متزامن وتحسين استجابة التطبيق.
في هذا المقال سنشرح خطوة بخطوة كيفية دمج RabbitMQ مع Django لبناء نظام django rabbitmq notifications احترافي، بداية من الفكرة، ثم الإعداد، ثم كتابة الكود، مع بعض أفضل الممارسات.
بشكل افتراضي، عند تنفيذ أي عملية ثقيلة داخل Django (مثل إرسال بريد إلكتروني أو إشعار Push أو اتصال خارجي)، فإنها تنفذ داخل نفس طلب HTTP. هذا يؤدي إلى:
بدلاً من ذلك، نريد:
بهذا الشكل نحصل على:
إذا كنت جديدًا على RabbitMQ وWork Queues، يمكنك مراجعة مقالنا: Work Queues في RabbitMQ: تنفيذ المهام الثقيلة في الخلفية.
فكرة الربط بين Django و RabbitMQ في حالة الإشعارات يمكن تلخيصها في 3 خطوات:
notifications_queue). بهذا الشكل، يقوم Django فقط بـ "جدولة" الإشعار، بينما وظيفة التنفيذ الحقيقية تتم في الخلفية.
قبل البدء، تأكد من توفر الآتي:
للتعامل مع الـ Background Tasks في Django بشكل عام، يمكنك الاطلاع أيضًا على: التعامل مع Background Tasks في Django وFastAPI.
نستخدم غالبًا مكتبة pika للتعامل مع RabbitMQ في بايثون:
pip install pika يمكنك استخدام مكتبات أخرى أو حتى أدوات جاهزة مثل Celery، لكن في هذا المقال سنبقى في مستوى أقرب لـ “Raw RabbitMQ” لفهم الفكرة الأساسية.
في ملف settings.py أضف إعدادات الاتصال بـ RabbitMQ (يفضل استخدامها من متغيرات البيئة):
# settings.py
RABBITMQ_HOST = "localhost"
RABBITMQ_PORT = 5672
RABBITMQ_USER = "guest"
RABBITMQ_PASSWORD = "guest"
RABBITMQ_NOTIFICATIONS_QUEUE = "notifications_queue" يمكنك لاحقًا فصل هذه القيم في ملف إعدادات مستقل أو استخدام بيئات مختلفة (development، production) كما شرحت سابقًا في: التحكم بالإصدارات في Django: إدارة migrations بشكل صحيح (نفس الفكرة في تنظيم الإعدادات).
من الجيد عدم كتابة كود الاتصال بـ RabbitMQ داخل الـ View مباشرة. الأفضل إنشاء ملف Helper أو Service لإعادة استخدامه لاحقًا.
لنفرض أن لدينا تطبيق داخل المشروع اسمه notifications. سننشئ ملف rabbitmq_producer.py بداخله:
# notifications/rabbitmq_producer.py
import json
import pika
from django.conf import settings
def get_rabbitmq_connection():
credentials = pika.PlainCredentials(
settings.RABBITMQ_USER,
settings.RABBITMQ_PASSWORD
)
parameters = pika.ConnectionParameters(
host=settings.RABBITMQ_HOST,
port=settings.RABBITMQ_PORT,
credentials=credentials
)
return pika.BlockingConnection(parameters)
def publish_notification(message: dict):
"""
إرسال رسالة إشعار إلى Queue في RabbitMQ.
message يجب أن يكون dict قابل للتحويل إلى JSON.
"""
connection = get_rabbitmq_connection()
channel = connection.channel()
# تأكد من وجود الـ Queue (idempotent)
channel.queue_declare(queue=settings.RABBITMQ_NOTIFICATIONS_QUEUE, durable=True)
body = json.dumps(message)
channel.basic_publish(
exchange="",
routing_key=settings.RABBITMQ_NOTIFICATIONS_QUEUE,
body=body.encode("utf-8"),
properties=pika.BasicProperties(
delivery_mode=2 # جعل الرسالة persistent
),
)
connection.close() بهذا الشكل يمكن لأي جزء داخل Django استدعاء publish_notification لجدولة إرسال إشعار في الخلفية.
عند تسجيل مستخدم جديد، نريد:
نفترض أن لدينا View لتسجيل المستخدم:
# users/views.py
from django.http import JsonResponse
from django.views.decorators.http import require_POST
from django.contrib.auth.models import User
from notifications.rabbitmq_producer import publish_notification
@require_POST
def register(request):
# قراءة البيانات من request.POST أو request.body
email = request.POST.get("email")
username = request.POST.get("username")
password = request.POST.get("password")
user = User.objects.create_user(
username=username,
email=email,
password=password
)
# إرسال رسالة إشعار إلى RabbitMQ
notification_message = {
"type": "WELCOME_EMAIL",
"to_email": email,
"username": username,
}
publish_notification(notification_message)
# إعادة response سريع للمستخدم
return JsonResponse({"status": "success", "message": "تم إنشاء الحساب بنجاح"}) لاحظ أن إرسال الإيميل الفعلي لم يتم هنا. فقط أرسلنا رسالة إلى الـ Queue. وظيفة الإرسال يقوم بها Worker في الخلفية.
الآن نحتاج سكربت Python يعمل خارج Django (أو مرتبط بمشروع Django) يستهلك رسائل notifications_queue ويقوم فعليًا بإرسال الإشعار.
لننشئ ملف اسمه notifications_worker.py في جذر المشروع أو داخل التطبيق:
# notifications/notifications_worker.py
import json
import os
import django
import pika
# إعداد Django إذا كان هذا الملف خارج manage.py
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project_name.settings")
django.setup()
from django.conf import settings
from django.core.mail import send_mail
def process_notification(message: dict):
"""
دالة تُنفّذ بناءً على نوع الإشعار.
"""
notification_type = message.get("type")
if notification_type == "WELCOME_EMAIL":
to_email = message.get("to_email")
username = message.get("username")
subject = "مرحبًا بك في موقعنا"
body = f"مرحبًا {username}، شكراً لتسجيلك في موقعنا."
send_mail(
subject,
body,
settings.DEFAULT_FROM_EMAIL,
[to_email],
fail_silently=False,
)
else:
# يمكن تسجيل أنواع أخرى أو تجاهلها
print(f"Unknown notification type: {notification_type}")
def main():
credentials = pika.PlainCredentials(
settings.RABBITMQ_USER,
settings.RABBITMQ_PASSWORD
)
parameters = pika.ConnectionParameters(
host=settings.RABBITMQ_HOST,
port=settings.RABBITMQ_PORT,
credentials=credentials
)
connection = pika.BlockingConnection(parameters)
channel = connection.channel()
channel.queue_declare(queue=settings.RABBITMQ_NOTIFICATIONS_QUEUE, durable=True)
print(" [*] Waiting for messages in notifications_queue. To exit press CTRL+C")
def callback(ch, method, properties, body):
try:
message = json.loads(body.decode("utf-8"))
process_notification(message)
# تأكيد استهلاك الرسالة
ch.basic_ack(delivery_tag=method.delivery_tag)
except Exception as e:
# في حالة حدوث خطأ يمكن تسجيله أو إعادة إرسال الرسالة
print(f"Error processing message: {e}")
# عدم إرسال ack يترك الرسالة في الـ Queue لإعادة المحاولة أو التعامل اليدوي
# التأكد من عدم إرسال أكثر من رسالة غير معالجة لنفس الـ Worker
channel.basic_qos(prefetch_count=1)
channel.basic_consume(
queue=settings.RABBITMQ_NOTIFICATIONS_QUEUE,
on_message_callback=callback,
)
channel.start_consuming()
if __name__ == "__main__":
main() يمكن تشغيل هذا الـ Worker عن طريق:
python -m notifications.notifications_worker أو تعديل المسار حسب هيكلة مشروعك.
ما سبق هو أبسط شكل للنظام. لكن عند الانتقال للإنتاج نحتاج إلى التفكير في:
email_notifications، sms_notifications). يمكنك إضافة حقل channel في الرسالة لتحديد نوع القناة:
{
"type": "ORDER_STATUS",
"channel": "EMAIL",
"to_email": "[email protected]",
"order_id": 123
} ثم في دالة process_notification تقوم بالتفريع حسب channel و type.
حاول دائمًا أن:
rabbitmq_producer.py.send_welcome_email_notification(user) تستدعي داخليًا publish_notification.يجب التفكير في:
في كثير من الأحيان، من الأفضل:
لا تقم بتثبيت قيم اتصال RabbitMQ في الكود مباشرة. استخدم متغيرات البيئة (ENV) خاصة في بيئة الإنتاج، وتأكد من وجود إعدادات مختلفة للتطوير والإنتاج.
RabbitMQ يوفر Management UI (غالبًا على المنفذ 15672) يمكنك استخدامه لمراقبة:
بهذا تتأكد أن نظام الإشعارات يعمل بشكل مستمر ولا تتراكم الرسائل بشكل غير طبيعي.
في مشاريع Django كثيرة، يتم استخدام Celery مع RabbitMQ أو Redis كأساس للـ Task Queue. Celery يوفر:
لكن الفائدة من المثال في هذا المقال هو:
وإذا أردت التوسع في تصميم Service كامل للإشعارات باستخدام RabbitMQ وMicroservices، راجع: تصميم Notification Service باستخدام RabbitMQ في Microservices.
user_id بدلاً من إرسال كل بيانات المستخدم. دمج Django مع RabbitMQ لإرسال الإشعارات بشكل غير متزامن يعطيك:
الخطوات الأساسية كانت:
settings.py.بهذا تكون قد وضعت الأساس لبناء نظام django rabbitmq notifications فعّال وقابل للتوسع، يمكن توسيعه لاحقًا ليدعم قنوات إشعار متعددة، وإدارة Retry، وتحليل أداء الإشعارات، وربطه مع خدمات أخرى في بنية Microservices أو نظام واحد متماسك.
شرح ربط Django مع RabbitMQ لإرسال الإشعارات في الخلفية وتحسين الأداء.
مساحة اعلانية