مقدمة: لماذا خادمك نائم بينما يجب أن يعمل
\n\nتخيل: المستخدم يضغط على زر "إرسال التقرير"، وتتجمد الصفحة لمدة 30 ثانية. الخادم يُنشئ PDF، ويضغط الصور، ويرسل البريد الإلكتروني — كل ذلك بشكل متزامن. مألوف؟ إذا كان الأمر كذلك، فقد وقعت في فخ الكود المُعطّل.
\n\nفي هذه المقالة، سنشرح كيف تحل قوائم المهام والخلفية في TypeScript هذه المشكلة. ستتعلم كيفية نقل العمل الثقيل إلى عمليات خلفية دون فقدان السيطرة على الأخطاء وقابلية التوسع. المحتوى مُعد للمطورين الذين يعرفون Node.js ويريدون رفع مشروعهم إلى المستوى التالي.
\n\nسنتحدث عن مكتبات Bull و BullMQ، ونتطرق إلى RabbitMQ، ونعرض أمثلة كود حقيقية في TypeScript. هيا بنا!
ما هي قائمة المهام ولماذا تحتاجها
\n\nقائمة المهام (task queue) هي آلية تفصل طلب المستخدم عن تنفيذه. بدلاً من معالجة العملية مباشرة في معالج HTTP، نرسل المهمة إلى قائمة، ويقوم عامل خلفي بأخذها وتنفيذها بشكل غير متزامن.
\n\nسيناريوهات الاستخدام النموذجية
\n- \n
- إرسال رسائل البريد الإلكتروني والإشعارات الفورية \n
- إنشاء PDF والصور والفيديو \n
- تحليل البيانات واستخراجها من الويب \n
- معالجة الطلبات في المتاجر الإلكترونية \n
- الحسابات والتقارير الضخمة \n
بدون قوائم المهام، قد ينهار تطبيقك تحت الضغط. معها، تحصل على تحمل الأخطاء (إذا تعطل العامل، لن تضيع المهمة) وقابلية التوسع (يمكن تشغيل 10 عمال بدلاً من واحد).
\n\nBull و BullMQ: قوائم مهام قوية لـ Node.js
\n\nBull هي المكتبة الأكثر شهرة للعمل مع قوائم المهام في نظام Node.js. تعتمد على Redis وتدعم TypeScript بشكل افتراضي. BullMQ هو خليفتها، أكثر حداثة، مع دعم الصناديق الرملية (العمليات المعزولة) وإدارة متقدمة.
\n\nالتثبيت والإعداد الأساسي
\nnpm install bullmq ioredis\nnpm install -D @types/bull\n\nلننشئ قائمة مهام وعاملاً في TypeScript:
\n\n// queue.ts\nimport { Queue } from 'bullmq';\n\nconst connection = {\n host: 'localhost',\n port: 6379,\n};\n\nexport const emailQueue = new Queue('email', { connection });\n\n// worker.ts\nimport { Worker } from 'bullmq';\nimport { sendEmail } from './emailService';\n\nconst worker = new Worker('email', async job => {\n const { to, subject, body } = job.data;\n await sendEmail(to, subject, body);\n console.log(`تم إرسال البريد الإلكتروني إلى ${to}`);\n}, { connection });\n\nworker.on('completed', job => {\n console.log(`المهمة ${job.id} مكتملة`);\n});\n\nworker.on('failed', (job, err) => {\n console.error(`فشلت المهمة ${job.id}: ${err.message}`);\n});\n\nالآن يمكننا إضافة مهمة من أي مكان في التطبيق:
\n\nimport { emailQueue } from './queue';\n\nawait emailQueue.add('send-welcome', {\n to: 'user@example.com',\n subject: 'مرحباً بك!',\n body: 'شكراً لتسجيلك...'\n});\n\nبسيط، أليس كذلك؟ لكن هذه مجرد البداية. BullMQ يوفر إمكانيات أكثر بكثير.
\n\nالإمكانيات المتقدمة في BullMQ
\n- \n
- إعادة المحاولة — إذا فشلت المهمة، يمكن إعادة تشغيلها تلقائياً بعد N ثانية. \n
- تأخير التنفيذ — تأجيل المهمة لوقت محدد. \n
- الأولويات — معالجة المهام العاجلة أولاً. \n
- الأحداث — يمكن الاشتراك في التقدم، الإكمال، الأخطاء. \n
- الصناديق الرملية — تنفيذ الكود في عملية معزولة (أمان + نقاء). \n
مثال مع إعادة المحاولة والتأخير:
\n\nawait emailQueue.add('send-invoice', {\n invoiceId: 123,\n email: 'client@example.com'\n}, {\n attempts: 5,\n backoff: { type: 'expon' }\n});\n\nهذا يضمن إرسال الفاتورة حتى في حالة فشل مؤقت للخادم.
\n\nRabbitMQ: بديل قوي للأنظمة الموزعة
\n\nإذا كنت تعمل في بيئة microservices أو تحتاج إلى توجيه معقد للرسائل، فإن RabbitMQ هو خيار ممتاز. على عكس Bull الذي يعتمد على Redis، RabbitMQ هو وسيط رسائل كامل (message broker) يدعم بروتوكول AMQP.
\n\nمثال بسيط باستخدام مكتبة amqplib مع TypeScript:
import amqp from 'amqplib';\n\nasync function sendMessage() {\n const connection = await amqp.connect('amqp://localhost');\n const channel = await connection.createChannel();\n const queue = 'tasks';\n\n await channel.assertQueue(queue, { durable: true });\n channel.sendToQueue(queue, Buffer.from('Hello World'), { persistent: true });\n console.log('تم إرسال الرسالة');\n}\n\nRabbitMQ يتفوق في السيناريوهات التي تتطلب توجيهاً معقداً، مثل توزيع المهام على عدة عمال بناءً على نوع المهمة.
\n\nأفضل الممارسات والنصائح للإنتاج
\n\n1. مراقبة القوائم
\nاستخدم أدوات مثل Bull Board (لوحة تحكم لـ Bull) أو RabbitMQ Management UI لمراقبة حالة المهام. هذا يساعدك على اكتشاف المشاكل مبكراً.
\n\n2. معالجة الأخطاء
\nلا تترك المهام الفاشلة دون معالجة. استخدم معالج failed لتسجيل الأخطاء وإعادة المحاولة عند الحاجة.
3. تحديد المهلة الزمنية
\nحدد مهلة زمنية للمهام لمنع تعليق العمال إلى الأبد:
\n\nconst worker = new Worker('email', async job => {\n // الكود هنا\n}, { \n connection,\n timeout: 30000 // 30 ثانية\n});\n\n4. استخدام السجلات
\nسجل كل خطوة في معالجة المهمة لتسهيل تصحيح الأخطاء. استخدم مكتبات مثل winston أو pino.
5. اختبار الأداء
\nاختبر نظامك تحت ضغط عالٍ باستخدام أدوات مثل artillery أو k6. تأكد من أن القوائم والعمال يمكنهم التعامل مع ذروة الطلبات.
الخلاصة
\n\nقوائم المهام والخلفية في TypeScript ليست رفاهية، بل ضرورة لأي تطبيق إنتاجي. سواء اخترت BullMQ لبساطته أو RabbitMQ لمرونته، فإنك تستثمر في استقرار وقابلية توسع تطبيقك.
\n\nابدأ اليوم بتنفيذ قائمة مهام بسيطة، ثم وسعها حسب احتياجاتك. تذكر: المستخدم لا يجب أن ينتظر أبداً. اجعل خادمك يعمل بذكاء، ليس بجهد.
\n\nهل لديك أسئلة؟ شاركها في التعليقات! ولا تنسَ متابعة PythonLib للمزيد من المقالات التقنية.