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

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

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

فجأة، تحول الهدوء في غرفة مراقبة السيرفرات إلى جحيم. الطلبات صارت بالمئات في الثانية الواحدة. لوحة المراقبة أمامي كانت عبارة عن خطوط حمراء بتطلع وبتنزل زي مؤشر جهاز القلب لمريض بيموت. طلبات تسجيل المستخدمين بتعطي خطأ 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 هو اللي يستنى بدالهم. 😉

أبو عمر

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

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

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

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

آخر المدونات

التوسع والأداء العالي والأحمال

كان مستخدمونا في الطرف الآخر من العالم ينتظرون إلى الأبد: كيف أنقذتنا شبكات توصيل المحتوى (CDN) من جحيم زمن الاستجابة المرتفع؟

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

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

من شبكة مثقوبة إلى حصن منيع: كيف أنقذتنا قواعد البيانات الرسومية من كابوس الاحتيال؟

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

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

ميزانيات الخطأ (Error Budgets): كيف أنهت كابوس مكالمات منتصف الليل وأنقذتنا من الإرهاق؟

كنا غارقين في مكالمات طوارئ ليلية لا تنتهي، فريق منهك والمنتج على المحك. في هذه المقالة، أشارككم قصة كيف أنقذنا مفهوم "ميزانيات الخطأ" (Error Budgets)...

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

كانت اجتماعاتنا الفردية استجواباً صامتاً: كيف حولنا الـ 1-on-1 من تقرير حالة ممل إلى محرك لنمو الفريق؟

أشارككم تجربتي كقائد فريق تقني في تحويل الاجتماعات الفردية (1-on-1s) من جلسات استجواب مملة إلى محادثات مثمرة تساهم في بناء الثقة وتطوير الفريق. هذه المقالة...

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

كانت اختباراتنا تصرخ ‘الذئب’: كيف قضينا على ‘الاختبارات المتقلبة’ (Flaky Tests) واستعدنا الثقة في خطوط الأنابيب؟

في هذه المقالة، أشارككم قصة من أرض المعركة البرمجية، وكيف تغلب فريقي على كابوس "الاختبارات المتقلبة" أو Flaky Tests. سنغوص في أسبابها الخفية، ونتعلم استراتيجيات...

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

كانت أصابعي تصرخ من التكرار: كيف أنقذتني ‘مقتطفات الشفرة’ (Code Snippets) من جحيم كتابة Boilerplate؟

أشارككم قصتي مع التكرار الممل في البرمجة وكيف غيرت "مقتطفات الشفرة" (Code Snippets) طريقة عملي تماماً. دليل عملي من مبرمج فلسطيني لزيادة إنتاجيتك والتخلص من...

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

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

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

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

كانت شفرتنا هرمًا من الهلاك: كيف أنقذتنا ‘شروط الحماية’ (Guard Clauses) من جحيم الـ if/else المتداخلة؟

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

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

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

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

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