قوائم الانتظار والمهام الخلفية في PHP: تسريع موقعك دون فقدان البيانات

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

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

ابدأ الدورة

كيف تمنع موقع PHP الخاص بك من "التجميد" أثناء العمليات الطويلة؟

\n\n

هل سبق لك أن انتظرت 30 ثانية حتى يرسل الموقع بريدًا إلكترونيًا للتأكيد؟ أو شاهدت الصفحة "تتجمد" أثناء إنشاء تقرير PDF؟ في عالم PHP، حيث يتم تنفيذ كل طلب بشكل متزامن عادةً، يمكن لهذه المهام أن تدمر تجربة المستخدم. لكن هناك حل مثبت — قوائم الانتظار والمهام الخلفية. فهي تسمح بنقل العمل "الثقيل" إلى الكواليس، مع إظهار رسالة فورية للمستخدم: "تم استلام طلبك!".

\n\n

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

\n\n

لماذا تحتاج إلى قوائم الانتظار في PHP؟

\n\n

يعمل PHP تقليديًا بنموذج "طلب-استجابة". ولكن عندما يحين وقت العمليات التي تستغرق أكثر من 1-2 ثانية (إرسال البريد الإلكتروني، معالجة الصور، إنشاء التقارير، Webhooks)، يواجه المستخدم انتهاء المهلة أو يشعر بالملل وهو ينظر إلى مؤشر التحميل. تحل قوائم الانتظار هذه المشكلة بشكل جذري:

\n\n
    \n
  • استجابة فورية — يحصل المستخدم على رد فوري، وتذهب المهمة الثقيلة إلى الخلفية.
  • \n
  • قابلية التوسع — يمكنك تشغيل عشرات العمال (workers) بالتوازي دون تغيير الكود.
  • \n
  • الموثوقية — إذا توقف العامل عن العمل، تبقى المهمة في قائمة الانتظار ويتم تنفيذها لاحقًا.
  • \n
  • موازنة الحمل — يتم تخفيف الأحمال القصوى من خلال التخزين المؤقت.
  • \n
\n\n

دعنا نرى كيفية تنفيذ ذلك عمليًا باستخدام ثلاث من أشهر الطرق.

\n\n

1. قوائم الانتظار باستخدام Redis: سهل وسريع

\n\n

Redis ليس مجرد مخبأ (Cache). بفضل هياكل البيانات (القوائم، المجموعات) وآلية Pub/Sub، فهو مثالي لقوائم الانتظار البسيطة. إذا كان لديك Redis بالفعل على الخادم، فهذه أسرع طريقة لتطبيق المهام الخلفية دون تثبيت خدمات إضافية.

\n\n

مثال: قائمة انتظار البريد الإلكتروني باستخدام Redis

\n\n

لنفترض أنه بعد تسجيل المستخدم، نحتاج إلى إرسال بريد ترحيبي. بدلاً من استدعاء mail() داخل المتحكم (Controller)، نضع المهمة ببساطة في قائمة الانتظار.

\n\n

الخطوة 1. إضافة مهمة إلى قائمة الانتظار (المنتج - Producer):

\n\n
<?php\n// producer.php\n$redis = new Redis();\n$redis->connect('127.0.0.1', 6379);\n\n$task = json_encode([\n    'type' => 'welcome_email',\n    'user_id' => 42,\n    'email' => 'user@example.com',\n    'created_at' => time()\n]);\n\n// RPUSH يضيف عنصرًا إلى نهاية القائمة\n$redis->rPush('email_queue', $task);\necho "تمت إضافة المهمة إلى قائمة الانتظار\\n";\n?>
\n\n

الخطوة 2. العامل (Worker) الذي يعالج المهام (المستهلك - Consumer):

\n\n
<?php\n// worker.php\n$redis = new Redis();\n$redis->connect('127.0.0.1', 6379);\n\necho "تم تشغيل العامل، في انتظار المهام...\\n";\n\nwhile (true) {\n    // BLPop يمنع التنفيذ حتى يظهر عنصر\n    $taskData = $redis->blPop('email_queue', 5); // مهلة 5 ثوانٍ\n    \n    if ($taskData) {\n        $task = json_decode($taskData[1], true);\n        \n        echo "جاري معالجة المهمة: {$task['type']} لـ {$task['email']}\\n";\n        \n        // محاكاة إرسال البريد\n        sleep(2);\n        \n        // هنا المنطق الحقيقي: mail(), SMTP إلخ.\n        echo "تم إرسال البريد إلى المستخدم {$task['user_id']}\\n";\n    }\n}\n?>
\n\n

التشغيل:

\n

يتم تشغيل العامل في الخلفية (عبر nohup أو Supervisor):

\n
php worker.php &
\n\n

الآن، عندما يسجل المستخدم، يقوم المتحكم (Controller) باستدعاء producer.php ويعيد الرد فورًا، بينما يعالج العامل رسائل البريد الإلكتروني بهدوء. يضمن Redis عدم فقدان المهمة، حتى إذا توقف العامل مؤقتًا.

\n\n

2. RabbitMQ: المعيار الصناعي

\n\n

إذا كان مشروعك ينمو

المدونات

توصيات الكتب