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

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

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

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

هذاك اليوم، عرفت إنه طريقتنا في معالجة الطلبات، الطريقة “المتزامنة” (Synchronous)، كانت قنبلة موقوتة وانفجرت في وجهنا. ومن قلب هذاك الجحيم، طلع الحل اللي أنقذنا: قوائم انتظار الرسائل (Message Queues).

المشكلة: جحيم العمليات المتزامنة

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

هذا بالضبط اللي كان يصير في تطبيقنا:

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

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

الحل السحري: ما هي قوائم انتظار الرسائل (Message Queues)؟

قائمة انتظار الرسائل هي ببساطة وسيط، زي ساعي البريد. بدل ما السيرفر الرئيسي يعمل كل الشغل البطيء بنفسه، هو بس بيكتب “رسالة” فيها تفاصيل المهمة المطلوبة (مثلاً: “ابعث إيميل ترحيب للمستخدم فلان”) وبيحطها في “صندوق بريد” خاص (اللي هو الـ Queue).

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

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

المكونات الأساسية للنظام

  • المنتج (Producer): هو الجزء من تطبيقك اللي بينشئ الرسالة وبيحطها في الـ Queue. في مثالنا، هو السيرفر الرئيسي (Web Server) بعد ما المستخدم يعمل تسجيل.
  • قائمة الانتظار (Queue): هي البنية التحتية اللي بتخزن الرسائل بشكل آمن ومؤقت لحين معالجتها. أشهر الأمثلة عليها RabbitMQ, Amazon SQS, Redis, Kafka.
  • المستهلك (Consumer/Worker): هو عملية أو سيرفر منفصل بيسحب الرسائل من الـ Queue وبيعالجها.

نصيحة أبو عمر: فكّر فيها زي مطعم ماكدونالدز. أنت بتطلب من الكاشير (Producer)، هو بياخد طلبك فوراً وبيعطيك رقم (استجابة سريعة). طلبك بيظهر على شاشة في المطبخ (Queue). والعمّال في المطبخ (Consumers) بيحضروا طلبك. أنت ما بتستنى عند الكاشير لحد ما وجبتك تجهز!

كيف طبقنا الحل عملياً؟

بعد ليلة الإطلاق الكارثية، حكيت للفريق: “يا جماعة، لازم نفصل العمليات الطويلة عن الطلب الرئيسي. لازم نستخدم Message Queue”. كان في شوية مقاومة في البداية “ليش نعقّد النظام؟”، بس بعد ما شرحت الفكرة، الكل اقتنع.

اخترنا وقتها نستخدم RabbitMQ لأنه مجاني ومفتوح المصدر وقوي جداً. وهذا هو التغيير اللي عملناه في كود عملية التسجيل:

قبل قوائم الانتظار (السيناريو الكارثي)

// Pseudocode
function handle_registration(request) {
    // 1. Validate data (fast)
    validate(request.body);

    // 2. Create user in DB (relatively fast)
    const user = db.users.create(request.body);

    // 3. Send welcome email (VERY SLOW!!!)
    // The request HANGS here for 3-5 seconds
    email_service.send_welcome_email(user.email);

    // 4. Finally, respond to user
    return response.ok("Registration successful!");
}

بعد قوائم الانتظار (السيناريو المنقذ)

استخدمنا مكتبة زي bullmq مع Redis (كمثال بسيط وسريع).

أولاً: الكود في السيرفر الرئيسي (المنتج – Producer)

// In your main web server (e.g., Express.js route)
import { Queue } from 'bullmq';

// Connect to our queue named 'email-queue'
const emailQueue = new Queue('email-queue');

async function handle_registration(request) {
    // 1. Validate data (fast)
    validate(request.body);

    // 2. Create user in DB (relatively fast)
    const user = db.users.create(request.body);

    // 3. Add a job to the queue (EXTREMELY FAST!)
    // We just package the data and send it. We don't wait.
    await emailQueue.add('send-welcome-email', { email: user.email, name: user.name });

    // 4. Respond to user IMMEDIATELY
    return response.ok("Registration successful! Check your email shortly.");
}

ثانياً: كود العامل المنفصل (المستهلك – Consumer/Worker)

هذا الكود بيشتغل في عملية منفصلة تماماً، ممكن حتى على سيرفر ثاني.

// In a separate worker.js file
import { Worker } from 'bullmq';

// This worker listens to the 'email-queue'
const worker = new Worker('email-queue', async (job) => {
    const { email, name } = job.data;
    console.log(`Processing job ${job.id}: Sending welcome email to ${name}`);

    // The actual (slow) email sending logic happens here,
    // safely, away from the user's request.
    try {
        await email_service.send_welcome_email(email);
        console.log(`Email sent successfully to ${email}`);
    } catch (error) {
        console.error(`Failed to send email to ${email}`, error);
        // We can add logic here to retry the job later
        throw error; // This will make the job fail and can be retried
    }
});

console.log("Email worker started and waiting for jobs...");

النتيجة؟ زمن الاستجابة لطلب التسجيل نزل من 5-10 ثواني إلى أقل من 50 ميللي ثانية. النظام صار قادر يستوعب آلاف الطلبات في نفس الوقت بدون أي مشكلة.

فوائد ما بعدها “نجدة”: مزايا لم نكن نحلم بها

لما طبقنا قوائم الانتظار، اكتشفنا فوائد تانية غير السرعة:

1. التوسع (Scalability)

لما زاد الضغط على المنصة مرة ثانية في موسم تخفيضات، لاحظنا إنه عدد الرسائل في الـ Queue بلش يزيد. شو كان الحل؟ ببساطة شغلنا كمان 5 “عمّال” (worker processes) بضغطة زر. ما احتجنا نلمس السيرفر الرئيسي. صار عنا جيش من العمّال بيسحبوا الرسائل من الـ Queue وبيعالجوها بالتوازي. هاي هي المرونة الحقيقية.

2. الموثوقية (Reliability)

شو بصير لو الـ worker اللي بيبعث الإيميلات “مات” فجأة (صار فيه خطأ برمجي مثلاً)؟ في السيناريو القديم، كان الطلب كله بيفشل والمستخدم بياخد رسالة خطأ. أما الآن، الرسالة بتضلها محفوظة في الـ Queue. لما نصلّح الـ worker ونشغله مرة ثانية، أو لو كان عنا worker ثاني شغال، هو رح ياخد الرسالة ويكمل الشغل. ما في بيانات بتضيع.

3. فصل الخدمات (Decoupling)

السيرفر الرئيسي ما عاد يعرف أي تفاصيل عن كيفية إرسال الإيميل. كل اللي بيعرفه إنه لازم يبعت رسالة للـ Queue اسمها “send-welcome-email”. هذا سمح لنا نغيّر خدمة الإيميلات اللي بنستخدمها أو نعدل على محتوى الإيميل بدون ما نلمس الكود تبع السيرفر الرئيسي. كل فريق صار يشتغل على الجزء تبعه باستقلالية.

نصائح من “قلب المعركة” – خبرة أبو عمر

  • ابدأ بسيطاً: مش دايماً بتحتاج نظام معقد زي Kafka. في كثير من الحالات، نظام قوائم بسيط مبني على Redis (زي BullMQ) أو خدمة مُدارة زي Amazon SQS بتكون كافية وزيادة. اختار الأداة المناسبة لحجم مشكلتك.
  • المراقبة ثم المراقبة: أهم مؤشر لازم تراقبه هو “طول قائمة الانتظار” (Queue Length). إذا كان عدد الرسائل في الـ Queue بيزيد بشكل مستمر، هاي إشارة إنه عدد الـ Consumers عندك مش كافي لمواكبة الـ Producers.
  • تعامل مع الفشل بحكمة: شو بصير لو رسالة فشلت معالجتها مرة ومرتين وعشرة؟ لا تخليها تضل تحاول للأبد وتحجز الـ worker. استخدم نمط اسمه “Dead Letter Queue” (DLQ). بعد عدد معين من المحاولات الفاشلة، انقل الرسالة لـ Queue منفصلة اسمها DLQ عشان تقدر تحللها وتشوف ليش فشلت بدون ما توقف باقي النظام.
  • اجعل عملياتك غير قابلة للتكرار (Idempotency): في عالم الـ Message Queues، ممكن (وفي حالات نادرة) إنه نفس الرسالة تتم معالجتها مرتين. لازم الكود تبع الـ worker يكون مصمم بحيث لو استقبل نفس المهمة مرتين، ما يسبب مشكلة. مثلاً، لو المهمة هي “خصم 10 دولار من رصيد المستخدم”، لازم تتأكد إنك ما بتخصمها مرتين. ممكن تعمل هاد الأشي عن طريق التحقق من رقم الطلب قبل تنفيذ العملية.

الخلاصة يا جماعة الخير 🏁

الدرس الأكبر اللي تعلمته هو: أي عملية بتاخد وقت طويل (أكثر من نصف ثانية) أو ممكن تفشل، ما لازم تكون جزء من طلب المستخدم المباشر. إرسال إيميلات، معالجة صور أو فيديوهات، إنشاء تقارير PDF، التواصل مع خدمات خارجية بطيئة… كل هاي مهام مكانها الطبيعي هو في “عامل” خلف الكواليس، بيتم استدعاؤه عن طريق قائمة انتظار الرسائل.

قوائم الانتظار مش مجرد أداة تقنية، هي تغيير في طريقة التفكير. هي اللي بتفصل بين النظام الهاوي اللي بينهار عند أول ضغط، والنظام المحترف القابل للتوسع اللي بيقدر يخدم ملايين المستخدمين. لا تخلي طلبات المستخدمين عندك تموت ببطء، خلّي الـ Queue هو اللي يستنى بدالهم. 😉

أبو عمر

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

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

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

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

آخر المدونات

الشبكات والـ APIs

واجهاتنا كانت تطلب بيانات لا تحتاجها: كيف أنقذنا GraphQL من جحيم الاستدعاءات المتعددة والبيانات الزائدة؟

أشارككم قصة حقيقية من تجربتي كمبرمج، وكيف عانينا من مشاكل الأداء بسبب واجهات REST API التقليدية. سأشرح لكم بالتفصيل كيف كانت تقنية GraphQL هي طوق...

24 أبريل، 2026 قراءة المزيد
الحوسبة السحابية

تطبيقاتنا كانت خاملة وندفع ثمنها: كيف أنقذتنا البنية غير الخادومية (Serverless) من جحيم الموارد المهدرة؟

أشارككم قصة حقيقية من تجربتي كمبرمج، وكيف كانت فواتير الحوسبة السحابية تستنزف ميزانيتنا بسبب خوادم خاملة. اكتشفوا معنا كيف كانت البنية غير الخادومية (Serverless) طوق...

24 أبريل، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

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

أنا أبو عمر، وهذا المقال هو قصة حقيقية عن كيف تحولت مقابلاتنا التقنية من فوضى قائمة على "التحيز" و"الحظ" إلى عملية منظمة وعادلة. سأشارككم بالتفصيل...

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

كان الربط مع البنوك كابوساً: كيف أنقذتنا ‘الخدمات المصرفية المفتوحة’ (Open Banking) من جحيم التكاملات المعقدة؟

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

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

بنيتنا التحتية كانت قصراً من ورق: كيف أنقذنا Terraform من جحيم التغييرات اليدوية

أشارككم قصة حقيقية من قلب المعركة مع السيرفرات، وكيف انتقلنا من الفوضى والتعديلات اليدوية الكارثية إلى بنية تحتية صلبة ومؤتمتة بالكامل باستخدام Terraform. هذه ليست...

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

كانت مساراتنا المهنية طريقاً مسدوداً: كيف أنقذتنا ‘مصفوفات الكفاءة’ من جحيم الركود الوظيفي؟

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

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

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

أشارككم قصة حقيقية من قلب المعركة البرمجية، كيف أنقذنا مشروعنا من أخطاء الواجهة الأمامية الكارثية باستخدام اختبار الانحدار البصري (Visual Regression Testing). مقالة عملية مع...

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