كانت ذروة المبيعات تقتل خوادمنا: كيف أنقذتنا ‘طوابير الرسائل’ من جحيم الطلبات المفقودة؟

يا جماعة الخير، السلام عليكم ورحمة الله. معكم أخوكم أبو عمر.

والله بتذكرها زي كأنها امبارح. ليلة “الجمعة البيضاء”، فريق التسويق عامل شغل نار، والإعلانات في كل مكان، والكل متوقع أرقام مبيعات تاريخية. إحنا كفريق تقني كنا محضرين حالنا، ضاعفنا عدد الخوادم (السيرفرات)، وعملنا كل الاختبارات الممكنة. كنا قاعدين في المكتب، القهوة شغالة والكل على أعصابه، بنراقب الشاشات والبيانات وهي بتطلع.

الساعة 12 بالليل… انفتح الباب. مش باب المكتب، لا… باب الجحيم. تدفق الطلبات كان زي تسونامي. الأرقام على شاشات المراقبة صارت مجنونة، والمؤشرات كلها باللون الأحمر. الخوادم بلشت تئن تحت الضغط، وقاعدة البيانات يا حرام كانت بتصرخ “ارحموني!”. وفجأة، بلشت توصلنا الرسائل من خدمة العملاء: “في زبائن بتحكي إنهم دفعوا والمصاري انخصمت بس ما وصلهم تأكيد طلب!”، “الموقع بعلّق عند الدفع!”، “صفحة المنتجات ما بتفتح!”.

كان كابوس حقيقي. العرق البارد بلش ينزل، وصوت إشعارات الأخطاء على نظام المراقبة كان زي مطرقة فوق راسنا. كنا بنخسر فلوس، وبنخسر ثقة عملائنا مع كل دقيقة بتمر. المشكلة ما كانت في قلة الموارد، المشكلة كانت في “الطريقة” اللي نظامنا مبني فيها. كل طلب شراء كان لازم يمر بسلسلة عمليات طويلة بنفس اللحظة: التحقق من الدفع، تحديث المخزون، إرسال إيميل تأكيد، إرسال رسالة SMS… وأي خطوة من هدول بتعلق أو بتفشل، الطلب كله بيضيع أو المستخدم بضل يستنى والصفحة بتحمّل. في هذيك الليلة، أقسمت إنه هاد الأشي ما رح يتكرر. ومن هنا، بدأت رحلتنا مع بطل قصتنا اليوم: طوابير الرسائل (Message Queues).

ما هي طوابير الرسائل؟ تخيلها مكتب بريد يا صاحبي

قبل ما ندخل في التفاصيل التقنية المعقدة، خليني أبسطلك الفكرة. تخيل إنك رحت على مكتب البريد عشان ترسل طرد. هل الموظف بياخد منك الطرد وبيروح هو بنفسه يوصله لبيت المستلم وبيرجع؟ طبعاً لا. هاد الأشي مش منطقي وبضيّع وقت.

اللي بصير هو إنه الموظف (خلّينا نسميه المنتج – Producer) بياخد منك الطرد، بحط عليه ختم، وبحطه في صندوق كبير مع طرود تانية (هاد هو الطابور – Queue). بعدين، بيجي عامل تاني (خلّينا نسميه المستهلك – Consumer)، بياخد الطرود من الصندوق حبة حبة، وببلش يوزعها حسب العناوين.

هذا بالضبط ما تفعله طوابير الرسائل في عالم البرمجيات. إنها وسيط، “صندوق بريد” رقمي، يفصل بين أجزاء النظام المختلفة. جزء يضع “الرسائل” (المهام) في الطابور، وجزء آخر يسحب هذه الرسائل وينفذها في الوقت المناسب له، بدون ما يعطلوا بعض.

مكونات نظام طوابير الرسائل

  • المنتج (Producer): هو جزء من تطبيقك الذي يُنشئ الرسالة ويضعها في الطابور. في قصتنا، كان هذا هو نظام الدفع عند إتمام الطلب.
  • الطابور (Queue): هو المخزن المؤقت الذي يحتفظ بالرسائل بالترتيب (عادةً First-In, First-Out). أشهر الأمثلة عليه: RabbitMQ, Apache Kafka, AWS SQS, Google Pub/Sub.
  • المستهلك (Consumer): هو خدمة أو عامل (Worker) منفصل، وظيفته الوحيدة هي مراقبة الطابور، وسحب الرسائل منه الواحدة تلو الأخرى، وتنفيذ المهمة المطلوبة.

مشكلتنا قبل الطابور: الاقتران الشديد (Tight Coupling)

لكي نفهم قوة طوابير الرسائل، يجب أن نرى كيف كان نظامنا يعمل من قبل. عندما كان العميل يضغط على زر “إتمام الشراء”، كان الخادم يقوم بكل شيء في نفس الطلب (طلب HTTP متزامن):

السيناريو القديم (Synchronous Hell):
1. المستخدم يضغط “شراء”.
2. الخادم يستقبل الطلب.
3. (انتظار) يتصل بخدمة الدفع للتحقق من البطاقة.
4. (انتظار) يحدّث كمية المنتج في قاعدة البيانات (داخل Transaction).
5. (انتظار) يرسل إيميل تأكيد للعميل عبر خدمة إيميلات خارجية.
6. (انتظار) يرسل رسالة SMS للعميل.
7. (انتظار) يرسل إشعارًا لتطبيق المستودع لتجهيز الطلب.
8. أخيرًا، يرسل ردًا للمستخدم بأن “الطلب تم بنجاح”.

هل رأيت كلمة (انتظار)؟ إذا كانت خدمة الإيميلات بطيئة (وهذا يحدث كثيرًا)، فسيظل المستخدم معلقًا ينتظر. إذا فشلت خدمة الـ SMS، قد يفشل الطلب بأكمله ويجب على المستخدم المحاولة مرة أخرى، مع احتمال خصم المبلغ مرتين! هذا يسمى “الاقتران الشديد”، حيث يعتمد كل جزء على الآخر بشكل مباشر وفوري.

الحل باستخدام الطابور: شغل مرتب وغير متزامن (Asynchronous)

بعد ليلة الكابوس تلك، قمنا بإعادة هيكلة العملية بالكامل. الآن، عندما يضغط العميل على “إتمام الشراء”، يحدث شيء مختلف تمامًا وأسرع بكثير.

السيناريو الجديد (Asynchronous Heaven):
1. المستخدم يضغط “شراء”.
2. الخادم يستقبل الطلب.
3. (سريع جدًا) يتصل بخدمة الدفع للتحقق من البطاقة (هذه الخطوة الوحيدة التي يجب أن تبقى متزامنة).
4. (فوري تقريبًا) يُنشئ رسالة تحتوي على كل تفاصيل الطلب (رقم الطلب، إيميل العميل، المنتجات…) ويضعها في “طابور الطلبات الجديدة”.
5. يرسل ردًا فوريًا للمستخدم: “تم استلام طلبك بنجاح وجارٍ معالجته! ستصلك رسالة تأكيد قريبًا.”

انتهى دور الخادم الرئيسي هنا! المستخدم سعيد لأن الموقع استجاب بسرعة، ونحن سعداء لأن الخادم أصبح متاحًا لخدمة مستخدم آخر.

لكن ماذا حدث للطلب؟ إنه الآن بأمان في الطابور. وفي الخلفية، لدينا جيش من “المستهلكين” المستعدين للعمل.

مثال بالكود (Python مع RabbitMQ)

هذا مثال مبسط جدًا لكود “المنتج” الذي يضع الرسالة في الطابور بعد إتمام الدفع.


# --- Producer (جزء من خادم الويب) ---
import pika
import json

def on_successful_payment(order_details):
    # الاتصال بـ RabbitMQ
    connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
    channel = connection.channel()

    # التأكد من وجود الطابور
    channel.queue_declare(queue='new_orders_queue', durable=True)

    # تحويل تفاصيل الطلب إلى رسالة
    message = json.dumps(order_details)

    # نشر الرسالة في الطابور
    channel.basic_publish(
        exchange='',
        routing_key='new_orders_queue',
        body=message,
        properties=pika.BasicProperties(
            delivery_mode=2,  # اجعل الرسالة ثابتة (تُحفظ على القرص)
        ))

    print(f" [x] Sent order {order_details['order_id']}")
    connection.close()

# عند الدفع الناجح
order_data = {'order_id': 123, 'user_email': 'user@example.com', 'product_id': 'xyz', 'amount': 99}
on_successful_payment(order_data)

والآن، كود “المستهلك” الذي يعمل على خادم منفصل ويقوم بالمهام الثقيلة.


# --- Consumer (يعمل كخدمة منفصلة) ---
import pika
import json
import time

def process_order(order_details):
    print(f"Processing order {order_details['order_id']}...")
    # 1. تحديث المخزون في قاعدة البيانات
    time.sleep(1) # محاكاة لعملية قاعدة البيانات
    print("  - Inventory updated.")
    
    # 2. إرسال إيميل تأكيد
    time.sleep(2) # محاكاة لبطء خدمة الإيميلات
    print(f"  - Confirmation email sent to {order_details['user_email']}.")
    
    # 3. إرسال إشعار للمستودع
    time.sleep(0.5)
    print("  - Warehouse notified.")
    print(f"Order {order_details['order_id']} processed successfully.")


connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.queue_declare(queue='new_orders_queue', durable=True)

def callback(ch, method, properties, body):
    order_data = json.loads(body)
    process_order(order_data)
    ch.basic_ack(delivery_tag=method.delivery_tag) # تأكيد استلام ومعالجة الرسالة

# استهلك رسالة واحدة كل مرة لضمان التوزيع العادل
channel.basic_qos(prefetch_count=1) 
channel.basic_consume(queue='new_orders_queue', on_message_callback=callback)

print(' [*] Waiting for new orders. To exit press CTRL+C')
channel.start_consuming()

الفوائد اللي جنيناها من هذا التغيير

بعد تطبيق هذا النظام، تغير كل شيء. في الجمعة البيضاء التالية، كنا نشرب القهوة ونبتسم ونحن نرى الأرقام ترتفع بهدوء.

  1. قابلية التوسع (Scalability) خرافية: هل لدينا ضغط كبير؟ بدلًا من زيادة خوادم الويب، كل ما علينا فعله هو تشغيل المزيد من “المستهلكين”. يمكننا زيادة 10 أو 20 أو 100 مستهلك لمعالجة الطابور بشكل أسرع، وعندما ينتهي الضغط، نقوم بإيقافهم. مرونة لا مثيل لها.
  2. المرونة وتحمل الأخطاء (Resilience): ماذا لو تعطلت خدمة إرسال الإيميلات؟ لا مشكلة! المستهلك سيحاول إرسال الإيميل، سيفشل، ولن يقوم بتأكيد استلام الرسالة (ack). ستبقى الرسالة في الطابور ليتم معالجتها لاحقًا عندما تعود الخدمة للعمل. لا طلبات مفقودة بعد اليوم!
  3. فصل الخدمات (Decoupling): أصبح فريق الويب يركز على تجربة المستخدم وسرعة الموقع، وفريق العمليات الخلفية يركز على تحسين كفاءة المستهلكين. كل فريق يعمل بمعزل عن الآخر، مما زاد من سرعة التطوير.
  4. تحسين تجربة المستخدم (UX): المستخدم يحصل على استجابة فورية. لا مزيد من شاشات التحميل الطويلة والقلق بشأن ما إذا كان الطلب قد تم أم لا.

نصائح أبو عمر – الزبدة 💡

من خبرتي في التعامل مع هذه الأنظمة، اسمحولي أقدم لكم شوية نصائح عملية:

  • ابدأ بسيطًا: لست بحاجة إلى Kafka من اليوم الأول. ابدأ بـ RabbitMQ أو AWS SQS. فهي أسهل في الإعداد والفهم، وتحل 90% من المشاكل الشائعة.
  • المراقبة هي روحك: يجب أن تراقب طول الطابور (Queue Length) باستمرار. إذا رأيت أن عدد الرسائل في الطابور يزداد بشكل مطرد ولا ينقص، فهذا يعني أن مستهلكيك أبطأ من منتجيك، وتحتاج إلى إضافة المزيد من المستهلكين.
  • اجعل عملياتك “Idempotent”: هذه كلمة مهمة وتعني أن تنفيذ العملية أكثر من مرة يعطي نفس النتيجة. قد يحدث (نادرًا) أن تتم معالجة نفس الرسالة مرتين. يجب أن يكون نظامك مصممًا للتعامل مع هذا. على سبيل المثال، قبل خصم المنتج من المخزون، تحقق أولًا إذا كان قد تم خصمه بالفعل لهذا الطلب.
  • استخدم طابور الرسائل الميتة (Dead-Letter Queue): ماذا تفعل برسالة فشلت معالجتها 10 مرات؟ لا تتركها في الطابور الرئيسي للأبد. قم بإعداد “طابور الرسائل الميتة” لتنتقل إليه هذه الرسائل الفاشلة تلقائيًا، حتى يتمكن المطورون من تحليلها يدويًا ومعرفة سبب المشكلة.

الخلاصة

يا جماعة، الانتقال إلى بنية تعتمد على طوابير الرسائل لم يكن مجرد حل تقني، بل كان تغييرًا في طريقة تفكيرنا كفريق. تعلمنا أن السرعة ليست دائمًا في تنفيذ كل شيء فورًا، بل في تنظيم العمل بذكاء. طوابير الرسائل حولت الفوضى إلى نظام، والضغط إلى فرصة للتوسع، وأنقذت سمعتنا وأعمالنا في أصعب الأوقات.

نصيحتي الأخيرة: لا تنتظر حتى تحترق خوادمك وتضيع طلبات عملائك. ابدأ في استكشاف عالم الأنظمة غير المتزامنة وطوابير الرسائل من اليوم. قد تكون هي المنقذ الذي لا تعلم أنك بحاجة إليه. 👍

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

التكنلوجيا المالية Fintech

من الصندوق الأسود إلى الشفافية: كيف فتحنا أبواب الثقة في التقييم الائتماني باستخدام XAI

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

29 مايو، 2026 قراءة المزيد
البنية التحتية وإدارة السيرفرات

كانت بيئاتنا نسخاً مشوهة: كيف أنقذتنا ‘البنية التحتية كوداً’ (IaC) من جحيم الانحراف التكويني؟

أشارككم قصة من قلب المعركة التقنية، عن ليلة كادت أن تودي بمشروع كامل بسبب "الانحراف التكويني". اكتشفوا كيف أصبحت "البنية التحتية كوداً" (IaC) وأدوات مثل...

29 مايو، 2026 قراءة المزيد
أدوات وانتاجية

كانت واجهة الأوامر تبطئني: كيف أنقذني ‘الباحث التقريبي’ (Fuzzy Finder) من جحيم البحث عن الملفات والأوامر؟

كنت أقضي دقائق ثمينة في البحث عن ملفات وأوامر قديمة في واجهة الأوامر، مما كان يقتل إنتاجيتي. في هذه المقالة، أشارككم قصتي مع أداة 'الباحث...

29 مايو، 2026 قراءة المزيد
​معمارية البرمجيات

ذاكرة فريقنا المعمارية قصيرة: كيف أنقذتنا ‘سجلات القرارات المعمارية’ (ADRs) من جحيم إعادة اكتشاف العجلة؟

أتذكر جيدًا تلك النقاشات العقيمة التي كانت تسرق أيامًا من عمر مشاريعنا، فقط لنعيد اكتشاف قرارات اتخذناها بالفعل ونسينا أسبابها. في هذه المقالة، أسرد لكم...

29 مايو، 2026 قراءة المزيد
البودكاست