قوائم المهام والخلفية في TypeScript: لا تسقط في الإنتاج

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: لماذا خادمك نائم بينما يجب أن يعمل

\n\n

تخيل: المستخدم يضغط على زر "إرسال التقرير"، وتتجمد الصفحة لمدة 30 ثانية. الخادم يُنشئ PDF، ويضغط الصور، ويرسل البريد الإلكتروني — كل ذلك بشكل متزامن. مألوف؟ إذا كان الأمر كذلك، فقد وقعت في فخ الكود المُعطّل.

\n\n

في هذه المقالة، سنشرح كيف تحل قوائم المهام والخلفية في TypeScript هذه المشكلة. ستتعلم كيفية نقل العمل الثقيل إلى عمليات خلفية دون فقدان السيطرة على الأخطاء وقابلية التوسع. المحتوى مُعد للمطورين الذين يعرفون Node.js ويريدون رفع مشروعهم إلى المستوى التالي.

\n\n

سنتحدث عن مكتبات Bull و BullMQ، ونتطرق إلى RabbitMQ، ونعرض أمثلة كود حقيقية في TypeScript. هيا بنا!

\n\n

ما هي قائمة المهام ولماذا تحتاجها

\n\n

قائمة المهام (task queue) هي آلية تفصل طلب المستخدم عن تنفيذه. بدلاً من معالجة العملية مباشرة في معالج HTTP، نرسل المهمة إلى قائمة، ويقوم عامل خلفي بأخذها وتنفيذها بشكل غير متزامن.

\n\n

سيناريوهات الاستخدام النموذجية

\n
    \n
  • إرسال رسائل البريد الإلكتروني والإشعارات الفورية
  • \n
  • إنشاء PDF والصور والفيديو
  • \n
  • تحليل البيانات واستخراجها من الويب
  • \n
  • معالجة الطلبات في المتاجر الإلكترونية
  • \n
  • الحسابات والتقارير الضخمة
  • \n
\n\n

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

\n\n

Bull و BullMQ: قوائم مهام قوية لـ Node.js

\n\n

Bull هي المكتبة الأكثر شهرة للعمل مع قوائم المهام في نظام Node.js. تعتمد على Redis وتدعم TypeScript بشكل افتراضي. BullMQ هو خليفتها، أكثر حداثة، مع دعم الصناديق الرملية (العمليات المعزولة) وإدارة متقدمة.

\n\n

التثبيت والإعداد الأساسي

\n
npm 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\n
import { 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\n

مثال مع إعادة المحاولة والتأخير:

\n\n
await emailQueue.add('send-invoice', {\n  invoiceId: 123,\n  email: 'client@example.com'\n}, {\n  attempts: 5,\n  backoff: { type: 'expon' }\n});
\n\n

هذا يضمن إرسال الفاتورة حتى في حالة فشل مؤقت للخادم.

\n\n

RabbitMQ: بديل قوي للأنظمة الموزعة

\n\n

إذا كنت تعمل في بيئة microservices أو تحتاج إلى توجيه معقد للرسائل، فإن RabbitMQ هو خيار ممتاز. على عكس Bull الذي يعتمد على Redis، RabbitMQ هو وسيط رسائل كامل (message broker) يدعم بروتوكول AMQP.

\n\n

مثال بسيط باستخدام مكتبة amqplib مع TypeScript:

\n\n
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\n

RabbitMQ يتفوق في السيناريوهات التي تتطلب توجيهاً معقداً، مثل توزيع المهام على عدة عمال بناءً على نوع المهمة.

\n\n

أفضل الممارسات والنصائح للإنتاج

\n\n

1. مراقبة القوائم

\n

استخدم أدوات مثل Bull Board (لوحة تحكم لـ Bull) أو RabbitMQ Management UI لمراقبة حالة المهام. هذا يساعدك على اكتشاف المشاكل مبكراً.

\n\n

2. معالجة الأخطاء

\n

لا تترك المهام الفاشلة دون معالجة. استخدم معالج failed لتسجيل الأخطاء وإعادة المحاولة عند الحاجة.

\n\n

3. تحديد المهلة الزمنية

\n

حدد مهلة زمنية للمهام لمنع تعليق العمال إلى الأبد:

\n\n
const worker = new Worker('email', async job => {\n  // الكود هنا\n}, { \n  connection,\n  timeout: 30000 // 30 ثانية\n});
\n\n

4. استخدام السجلات

\n

سجل كل خطوة في معالجة المهمة لتسهيل تصحيح الأخطاء. استخدم مكتبات مثل winston أو pino.

\n\n

5. اختبار الأداء

\n

اختبر نظامك تحت ضغط عالٍ باستخدام أدوات مثل artillery أو k6. تأكد من أن القوائم والعمال يمكنهم التعامل مع ذروة الطلبات.

\n\n

الخلاصة

\n\n

قوائم المهام والخلفية في TypeScript ليست رفاهية، بل ضرورة لأي تطبيق إنتاجي. سواء اخترت BullMQ لبساطته أو RabbitMQ لمرونته، فإنك تستثمر في استقرار وقابلية توسع تطبيقك.

\n\n

ابدأ اليوم بتنفيذ قائمة مهام بسيطة، ثم وسعها حسب احتياجاتك. تذكر: المستخدم لا يجب أن ينتظر أبداً. اجعل خادمك يعمل بذكاء، ليس بجهد.

\n\n

هل لديك أسئلة؟ شاركها في التعليقات! ولا تنسَ متابعة PythonLib للمزيد من المقالات التقنية.

المدونات

توصيات الكتب