كانت طلباتنا تنهار تحت الضغط: كيف أنقذتنا ‘طوابير الرسائل’ (Message Queues) من جحيم المعالجة المتزامنة؟

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

اسمحولي اليوم أحكيلكم قصة صارت معي ومع فريقي قبل كم سنة، قصة فيها شوية توتر، وشوية قهوة زيادة، وكثير من الدروس. كنا وقتها شغالين على نظام متجر إلكتروني لأحد العملاء، وكان شغله “ماشي زي الحلاوة”. لكن زي ما بحكوا، “دوام الحال من المحال”.

أجى يوم الجمعة، والعميل قرر يعمل حملة تخفيضات ضخمة، “جمعة التوفير” زي ما سماها. إحنا كفريق تقني كنا متوقعين ضغط، لكن اللي صار كان فوق كل التوقعات. مع أول دقيقة من إطلاق العروض، صارت الطلبات تنهال على النظام زي المطر. في البداية كنا مبسوطين، لكن الفرحة ما دامت طويلة. فجأة، بدأت توصلنا التنبيهات: “Server CPU at 99%”, “Database connection timeouts”, وشكاوي المستخدمين بدأت تظهر على وسائل التواصل الاجتماعي: “الموقع معلّق!”، “دفعت وما وصلني تأكيد!”، “طلبي تكرر مرتين!”.

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

ما هي المشكلة بالضبط؟ فهم المعالجة المتزامنة (Synchronous)

عشان نفهم الحل، لازم نفصّل المشكلة أول. في الوضع الطبيعي اللي كنا شغالين عليه (المتزامن)، كانت دورة حياة الطلب كالتالي:

  1. المستخدم يضغط “إتمام الشراء”.
  2. المتصفح يرسل طلبًا إلى السيرفر.
  3. السيرفر يستقبل الطلب ويبدأ بالعمل:
    • يتأكد من صحة بيانات الطلب.
    • يخصم المبلغ من بوابة الدفع.
    • يحدّث كمية المنتج في قاعدة البيانات.
    • ينشئ فاتورة بصيغة PDF (عملية بطيئة).
    • يرسل بريدًا إلكترونيًا للعميل مع الفاتورة (عملية ممكن تكون بطيئة).
    • يسجل الطلب في نظام إدارة المستودعات.
  4. بعد انتهاء كل هذه الخطوات، السيرفر يرسل ردًا للمتصفح: “تم طلبك بنجاح”.

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

طوق النجاة: المعالجة غير المتزامنة وطوابير الرسائل (Asynchronous & Message Queues)

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

ببساطة، طابور الرسائل هو زي ساعي بريد رقمي أو حزام ناقل في مصنع. بدل ما السيرفر يعمل كل الشغل الثقيل بنفسه، صار يعمل شغلة وحدة بس وبسرعة: يستقبل الطلب، ويحط “رسالة” فيها تفاصيل الطلب في طابور، ويرجع للمستخدم فورًا ويقوله: “تمام، استلمنا طلبك وجاري معالجته!”.

كيف تغيرت بنية النظام؟

النظام الجديد صار مقسوم لجزئين رئيسيين:

  1. المنتِج (Producer): وهو السيرفر تبعنا. وظيفته الأساسية هي استقبال الطلبات من المستخدمين، التحقق منها بسرعة، ثم “إنتاج” رسالة ووضعها في الطابور.
  2. المستهلِك (Consumer) أو العامل (Worker): وهو عبارة عن برنامج منفصل تمامًا، كل وظيفته في الحياة إنه يراقب الطابور. كل ما توصل رسالة جديدة، بسحبها وببدأ ينفذ المهام البطيئة (إنشاء الفاتورة، إرسال الإيميل، …إلخ) بهدوء وبدون ما يضغط على السيرفر الأساسي.

باستخدام مثال السوبرماركت، الآن صار عندك كاشير سريع جدًا وظيفته فقط ياخد منك الحساب (هذا هو الـ Producer). بعدين بحط أغراضك على حزام ناقل (هذا هو الـ Message Queue). وفي الخلف، عندك عدة عمال (هذول هم الـ Consumers/Workers) كل واحد فيهم بياخد سلة من الحزام الناقل وببدأ يغلفها ويجهزها. النتيجة؟ الكاشير دائمًا فاضي لاستقبال زبون جديد، والطابور بيمشي بسرعة، والكل مبسوط.

مثال عملي: من كود متزامن إلى كود يستخدم طوابير الرسائل

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

الطريقة القديمة (المتزامنة) – الكود الذي يسبب الكوارث


# هذا مثال بسيط باستخدام فلاسك (Flask)
from flask import Flask, request
from slow_operations import generate_invoice_pdf, send_confirmation_email

app = Flask(__name__)

@app.route('/orders', methods=['POST'])
def create_order():
    order_data = request.get_json()
    
    # 1. حفظ الطلب في قاعدة البيانات
    order_id = db.save_order(order_data)
    
    # 2. تنفيذ العمليات البطيئة (هنا الكارثة)
    try:
        # هذه العملية قد تستغرق 5 ثواني
        invoice = generate_invoice_pdf(order_id) 
        
        # هذه العملية قد تستغرق ثانيتين
        send_confirmation_email(order_data['email'], invoice) 
    except Exception as e:
        # إذا فشل أي شيء هنا، قد نقع في مشكلة بيانات غير متناسقة
        return {"error": "Failed to process order"}, 500

    # 3. الرد على المستخدم بعد وقت طويييل
    return {"message": "Order created successfully!", "order_id": order_id}, 201

المشكلة واضحة: المستخدم رح ينتظر 7 ثواني (أو أكثر) حتى يحصل على رد. وإذا كان عنا 100 مستخدم بيطلبوا بنفس الوقت، فالسيرفر رح يكون محجوز بالكامل.

الطريقة الجديدة (غير المتزامنة) – الكود الذي ينقذ الموقف

الآن، رح نستخدم طابور رسائل (مثل RabbitMQ أو AWS SQS). رح نقسم الكود لجزئين.

الجزء الأول: السيرفر (Producer) – سريع وخفيف


# السيرفر الآن أسرع بكثير
from flask import Flask, request
import pika # مكتبة للتعامل مع RabbitMQ
import json

app = Flask(__name__)
# (هنا يتم إعداد الاتصال مع طابور الرسائل)
queue_connection = pika.BlockingConnection(...) 
channel = queue_connection.channel()
channel.queue_declare(queue='invoice_tasks')

@app.route('/orders', methods=['POST'])
def create_order_async():
    order_data = request.get_json()
    
    # 1. حفظ الطلب في قاعدة البيانات (بحالة "تحت المعالجة")
    order_id = db.save_order(order_data, status="PENDING")
    
    # 2. إنشاء رسالة ووضعها في الطابور
    message = {
        "order_id": order_id,
        "user_email": order_data['email']
    }
    channel.basic_publish(exchange='',
                          routing_key='invoice_tasks',
                          body=json.dumps(message))
    
    # 3. الرد على المستخدم فورا! (العملية كلها تأخذ أجزاء من الثانية)
    return {"message": "Order received and is being processed."}, 202

الجزء الثاني: العامل (Consumer) – البرنامج الذي يعمل في الخلفية


# هذا سكربت منفصل يعمل باستمرار (worker.py)
import pika
import json
from slow_operations import generate_invoice_pdf, send_confirmation_email

# (إعداد الاتصال مع الطابور)
queue_connection = pika.BlockingConnection(...)
channel = queue_connection.channel()
channel.queue_declare(queue='invoice_tasks')

def callback(ch, method, properties, body):
    print(" [x] Received new task")
    data = json.loads(body)
    order_id = data['order_id']

    try:
        # 3. تنفيذ العمليات البطيئة هنا
        invoice = generate_invoice_pdf(order_id)
        send_confirmation_email(data['user_email'], invoice)
        
        # 4. تحديث حالة الطلب في قاعدة البيانات إلى "مكتمل"
        db.update_order_status(order_id, "COMPLETED")

        print(f" [✔] Task for order {order_id} is done.")
        ch.basic_ack(delivery_tag=method.delivery_tag) # تأكيد إتمام المهمة
    except Exception as e:
        print(f" [!] Failed to process task for order {order_id}: {e}")
        # هنا يمكن التعامل مع الأخطاء، مثلا إعادة الرسالة للطابور أو وضعها في طابور آخر للمراجعة
        ch.basic_nack(delivery_tag=method.delivery_tag, requeue=False)


# إخبار العامل بالبدء في الاستماع للرسائل
channel.basic_consume(queue='invoice_tasks', on_message_callback=callback)
print(' [*] Waiting for tasks. To exit press CTRL+C')
channel.start_consuming()

بهذه الطريقة، صار السيرفر تبعنا حر ويستطيع استقبال آلاف الطلبات في الدقيقة، بينما العمال في الخلفية يقومون بالشغل الثقيل. إذا زاد الضغط، شو بنعمل؟ ببساطة، بنشغّل كمان نسخة من `worker.py`. بدل عامل واحد، بصير عنا 5 أو 10 أو 100 عامل بسحبوا من نفس الطابور، وبهيك بنكون قادرين على التوسع (Scale) بسهولة وبتكلفة أقل.

نصائح أبو عمر العملية من قلب المعركة

يا جماعة، الشغل النظري حلو، بس الميدان إله حسابات تانية. من خبرتي، هاي شوية نصائح ذهبية لما تشتغلوا مع طوابير الرسائل:

  • صمم عمالك ليكونوا “Idempotent”: هاي كلمة إنجليزية مهمة (Idempotency) ومعناها إنك لو شغّلت نفس المهمة بنفس البيانات مرتين أو ثلاثة، النتيجة النهائية لازم تكون وحدة. ليش؟ لأنه ممكن العامل يسحب رسالة، يبدأ يعالجها، وبعدين “يموت” (يعلّق أو السيرفر اللي هو عليه يعمل ريستارت) قبل ما يخبر الطابور إنه خلّص. في هاي الحالة، الطابور رح يعطي نفس الرسالة لعامل ثاني. لازم العامل الجديد لما يبدأ يتأكد أول: هل هاي الفاتورة انعملت قبل هيك؟ هل الإيميل انبعت؟ عادةً بنحلها عن طريق التحقق من حالة الطلب في قاعدة البيانات قبل البدء.
  • راقب طول الطابور: أهم مؤشر عندك هو طول الطابور (Queue Length). إذا شفت عدد الرسائل في الطابور بزيد مع الوقت وما بنقص، فهذا معناه إن المنتجين أسرع من المستهلكين. لازم يا إما تزيد عدد العمال (Consumers)، أو تشوف طريقة تسرّع فيها معالجة الرسالة نفسها.
  • استخدم طابور الرسائل الميتة (Dead Letter Queue – DLQ): شو بصير لو في رسالة كل ما عامل يحاول يعالجها بتفشل؟ (مثلاً بسبب خطأ في البيانات اللي فيها). ما بدك إياها تضل ترجع للطابور الرئيسي وتعمل “تسمم” وتمنع الرسائل السليمة من المعالجة. الحل هو إعداد DLQ. أي رسالة بتفشل عدد معين من المرات، بتنتقل تلقائيًا على هذا الطابور “الميت” عشان المطورين يقدروا يشوفوها بعدين ويحللوا سبب المشكلة.
  • “ما تعقّدها وهي بسيطة”: عالم طوابير الرسائل واسع. في أدوات بسيطة زي RabbitMQ أو خدمات سحابية سهلة زي AWS SQS، وفي وحوش زي Apache Kafka. ابدأ بالبسيط اللي بحل مشكلتك. مش كل تطبيق بحتاج Kafka. افهم متطلباتك واختار الأداة المناسبة.

الخلاصة: متى يجب أن تفكر في طوابير الرسائل؟

طوابير الرسائل ليست حلًا لكل المشاكل، لكنها تصبح ضرورية جدًا في حالات معينة:

  • عندما يكون لديك مهام تستغرق وقتًا طويلاً (مثل معالجة الصور والفيديو، إنشاء التقارير، إرسال الإيميلات).
  • عندما تريد فصل أجزاء نظامك عن بعضها (Decoupling) بحيث إذا فشل جزء، لا ينهار النظام بأكمله.
  • عندما تحتاج إلى التعامل مع تدفق كبير ومفاجئ من الطلبات (Spikes in traffic) دون أن ينهار نظامك.
  • عندما تريد أن تبني نظامًا قابلاً للتوسع الأفقي (Horizontal Scaling) بكفاءة وبتكلفة معقولة.

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

بالتوفيق يا أبطال! 💪

أبو عمر

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

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

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

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

آخر المدونات

التوظيف وبناء الهوية التقنية

كانت سيرتي الذاتية تصرخ في الفراغ: كيف أنقذت ‘العلامة التجارية الشخصية المتخصصة’ طلبي من الثقب الأسود لأنظمة ATS؟

كنت أرسل عشرات السير الذاتية دون أي رد، وكأنها تتبخر في الهواء. في هذه المقالة، أسرد لكم قصتي مع أنظمة تتبع المتقدمين (ATS) وكيف كان...

5 مايو، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

من الحظ إلى الخوارزميات: كيف أنقذ التسجيل الائتماني البديل الشركات الناشئة من تحيز البنوك؟

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

5 مايو، 2026 قراءة المزيد
ادارة الفرق والتنمية البشرية

كان النمو الوظيفي يعتمد على الحظ: كيف أنقذتنا ‘مصفوفات الكفاءات’ من جحيم استنزاف المواهب؟

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

4 مايو، 2026 قراءة المزيد
اختبارات الاداء والجودة

كانت تغطية اختباراتنا 100% لكنها وهمية: كيف أنقذنا ‘الاختبار الطفري’ (Mutation Testing) من جحيم الثقة الزائفة؟

كنا نظن أن تغطية اختبارات بنسبة 100% هي درعنا الحصين، حتى كشف لنا "الاختبار الطفري" (Mutation Testing) عن ثغرات قاتلة في جودة اختباراتنا. هذه قصتي...

4 مايو، 2026 قراءة المزيد
أتمتة العمليات

كانت سجلات التغيير تُكتب بالدموع: كيف أنقذتنا ‘الالتزامات التقليدية’ من جحيم كتابة ملاحظات الإصدار؟

تعبت من قضاء ساعات في كتابة سجلات التغيير يدويًا؟ يشارك أبو عمر قصة من قلب المعاناة وكيف حولت 'الالتزامات التقليدية' (Conventional Commits) الفوضى إلى نظام...

4 مايو، 2026 قراءة المزيد
نصائح برمجية

كان كل تغيير في البيانات مغامرة: كيف أنقذتنا ‘اللامتغيرية’ (Immutability) من جحيم الآثار الجانبية؟

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

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