Laravel Octane على سيرفرك: متى يتضاعف الأداء فعلاً ومتى لا يستحق

Octane مع Swoole يبقي التطبيق محمّلاً في الذاكرة فيقفز بمعدل الطلبات من مئات إلى آلاف في الثانية. لكنه يفتح فخاخ الحالة المقيمة، ويحتاج إعداداً على السيرفر، وأحياناً OPcache وتحسين الاستعلامات يكفيان.
Laravel Octane يغيّر طريقة تشغيل تطبيقك جذرياً: بدل أن تُقلع Laravel من الصفر مع كل طلب (تحميل كل الملفات، بناء الحاوية، قراءة الإعدادات) ثم تُرمى في نهايته، يبقي Octane التطبيق محمّلاً في الذاكرة عبر خادم تطبيقات مثل Swoole أو FrankenPHP، فيخدم كل طلب من نسخة جاهزة سلفاً. النتيجة عملياً: تطبيق نموذجي على PHP-FPM يخدم نحو 200 إلى 400 طلب في الثانية قد يقفز مع Octane/Swoole إلى 1,500 إلى 3,000 طلب في الثانية على نفس العتاد، أي تحسّن يتراوح غالباً بين 3 و8 أضعاف في السيناريوهات المناسبة، لا مجرد مضاعفة. الجواب المباشر: Octane يستحق حين يكون العبء الأكبر في الإقلاع المتكرر لا في قاعدة البيانات، أي التطبيقات كثيرة الطلبات الخفيفة (APIs، لوحات تحكم نشطة). لكنه لا يستحق العناء إن كان بطؤك سببه استعلامات ثقيلة أو غياب فهرسة أو غياب OPcache، لأن Octane لن يسرّع استعلاماً بطيئاً، بل قد يخفي مشكلة أعمق. الأهم: Octane يفتح فئة جديدة من الأخطاء اسمها تسرّب الحالة المقيمة في الذاكرة (memory state leakage) لأنك لم تعد تبدأ من صفحة نظيفة كل طلب.
القاعدة الجوهرية: قبل Octane، فعّل الأساسيات المجانية أولاً (OPcache، تحسين الاستعلامات، التخزين المؤقت)، فإن بقي الإقلاع هو العنق، فحينها Octane يعطيك قفزته الحقيقية.
لماذا يسرّع Octane بهذا القدر
في نموذج PHP-FPM التقليدي، كل طلب HTTP يمرّ بدورة كاملة: يقرأ PHP عشرات آلاف الأسطر من إطار العمل، يبني حاوية الخدمات (service container)، يسجّل مزوّدي الخدمات (service providers)، يقرأ ملفات الإعداد، ثم بعد كل ذلك ينفّذ سطراً واحداً من منطقك. هذا الإقلاع قد يستهلك من 20 إلى 60 ميلي ثانية لكل طلب حتى قبل لمس قاعدة البيانات.
Octane يدفع هذه التكلفة مرة واحدة عند بدء العامل (worker)، ثم يحتفظ بالتطبيق المُقلَع في الذاكرة. الطلب التالي يجد كل شيء جاهزاً، فيبدأ من منطقك مباشرة. عمّال Swoole يعملون بنموذج حلقة أحداث غير محجوبة (event loop)، ويستطيع العامل الواحد خدمة آلاف الاتصالات. هنا مصدر القفزة من مئات إلى آلاف الطلبات في الثانية.
| المقياس | PHP-FPM تقليدي | Octane + Swoole |
|---|---|---|
| الإقلاع لكل طلب | 20 إلى 60 ميلي ثانية | مرة واحدة عند بدء العامل |
| الطلبات/ثانية (API خفيف) | ~200 إلى 400 | ~1,500 إلى 3,000 |
| زمن الاستجابة (p50) | مرتفع بسبب الإقلاع | ينخفض بشكل ملموس |
| نموذج التنفيذ | عملية لكل طلب | عامل مقيم بحلقة أحداث |
فخاخ الحالة المقيمة في الذاكرة
هذا أخطر ما في Octane، وسبب أن بعض التطبيقات تنهار عليه رغم أنها تعمل على PHP-FPM. لأن التطبيق يبقى حياً بين الطلبات، أي حالة تتركها في الذاكرة تتسرّب إلى الطلب التالي، وأحياناً إلى مستخدم آخر تماماً. انتبه لهذه المصادر:
- الخصائص الساكنة (static properties): متغير static يحمل بيانات مستخدم من طلب سابق قد يظهر لمستخدم لاحق. هذه ثغرة تسريب بيانات لا مجرد خطأ أداء.
- الـSingletons التي تحمل حالة الطلب: خدمة سجّلتها كـsingleton وخزّنت فيها المستخدم الحالي ستبقى بذلك المستخدم للطلب التالي.
- تسرّب الذاكرة التدريجي: بيانات تتراكم في مصفوفات أو ذاكرة تخزين مؤقت داخل العملية دون تنظيف، فيكبر استهلاك العامل حتى ينهار. الحل ضبط إعادة تدوير العامل بعد عدد طلبات محدد (max_requests).
- الحاوية الملوّثة: ربطات أضفتها للحاوية أثناء طلب تبقى للطلب التالي ما لم تُنظَّف.
Octane يوفّر أحداثاً مثل RequestReceived ومهام تنظيف، ويعيد ضبط بعض الخدمات تلقائياً، لكنه لا يعرف حالتك المخصصة. القاعدة: لا تخزّن حالة خاصة بالطلب في مكان يعيش أطول من الطلب.
متطلبات الإعداد على Ubuntu VPS
على الخادم السحابي بنظام Ubuntu، إعداد Octane مع Swoole مباشر:
- PHP 8.2 أو أحدث مع إضافة Swoole عبر pecl install swoole، أو استخدام FrankenPHP الذي لا يحتاج إضافة PECL.
- تثبيت الحزمة: composer require laravel/octane ثم php artisan octane:install.
- تشغيل الخادم خلف Nginx كوكيل عكسي (reverse proxy) يمرّر الطلبات إلى منفذ Octane.
- إدارة العملية بـsupervisor أو systemd لإعادة التشغيل التلقائي عند السقوط، وضبط عدد العمّال بحسب عدد الأنوية.
- قاعدة عملية للعمّال: من 2 إلى 4 عمّال لكل vCPU نقطة بداية معقولة، ثم اضبط بالقياس.
كم موارد تحتاج
لأن كل عامل يحمل نسخة كاملة من التطبيق في الذاكرة، Octane يستهلك ذاكرة أكثر من FPM. باقة نمو (2vCPU/4GB) نقطة بداية جيدة لـAPI متوسط، وباقة أعمال (4vCPU/8GB) تعطي متسعاً لعمّال أكثر وذاكرة تخزين مؤقت أوسع. إن احتجت ذاكرة إضافية فإضافة +2GB RAM بـ600 ريال سنوياً حل مباشر. راجع الأسعار والباقات للتفاصيل.
متى يكفي OPcache وتحسين الاستعلامات
قبل أن تدفع تكلفة تعقيد Octane، افحص إن كانت مشكلتك أرخص حلاً. الأدوات المجانية غالباً تعطيك الجزء الأكبر من المكسب:
| الحل | التكلفة | المكسب المتوقع |
|---|---|---|
| تفعيل OPcache | سطر إعداد، مجاني | يخزّن الكود المصرّف، تحسّن ملموس فوري على أي إعداد |
| فهرسة الاستعلامات البطيئة | تحليل EXPLAIN، مجاني | عشرات إلى مئات الأضعاف لاستعلام واحد بطيء |
| التخزين المؤقت (cache) | إعداد بسيط | يزيل الحساب المتكرر تماماً |
| Laravel Octane | إعداد سيرفر + مراجعة كود الحالة | 3 إلى 8 أضعاف للطلبات كثيرة الإقلاع |
القاعدة الحاسمة: إن كان زمن استجابتك يقضيه التطبيق في انتظار قاعدة البيانات، فOctane لن يساعد لأن المشكلة ليست في الإقلاع. فعّل OPcache أولاً (يكاد يكون مكسباً مجانياً دائماً)، حسّن استعلاماتك بـEXPLAIN، ضع طبقة تخزين مؤقت، ثم قِس. إن بقي الإقلاع هو العنق بعد ذلك، فحينها Octane يعطيك قفزته الحقيقية دون أن يخفي مشكلة.
خوادم تشغيل Octane: Swoole وFrankenPHP وRoadRunner
Octane واجهة موحّدة فوق ثلاثة خوادم تطبيقات، لكل منها طبع مختلف:
| الخادم | المتطلب | يتميّز بـ |
|---|---|---|
| Swoole | إضافة PECL على PHP | الأنضج أداءً، مهام غير متزامنة، مؤقتات، برك اتصالات داخلية |
| FrankenPHP | ثنائي واحد (مبني على خادم Caddy) | لا إضافة PECL، يدعم HTTP/2 وHTTP/3 وTLS تلقائياً |
| RoadRunner | ثنائي مكتوب بلغة Go | مستقر، إدارة عمّال قوية، لا يحتاج تعديل PHP |
Swoole يعطي أعلى سقف أداء وأغنى ميزات (تنفيذ متزامن، مؤقتات دورية داخل العملية)، لكنه إضافة على PHP تحتاج صلاحية جذر لتثبيتها. FrankenPHP الأسهل نشراً لأنه ثنائي واحد يحمل خادم الويب معه، ويعطيك HTTP/3 دون إعداد Nginx منفصل. RoadRunner خيار وسط ثابت لمن يفضّل عزل طبقة العامل عن PHP نفسه. لأغلب الحالات الجديدة، FrankenPHP أقصر طريق للتجربة، وSwoole الوجهة حين تريد آخر قطرة أداء أو ميزاته المتزامنة.
كيف تقيس قبل وبعد بصدق
لا تصدّق تحسّناً لم تقسه على حملك أنت. اتبع هذه المنهجية: ثبّت سيناريو اختبار يشبه حركتك الحقيقية (نفس المسارات ونسب الطلبات)، شغّل أداة تحميل مثل wrk أو k6 بعدد اتصالات متزامنة ثابت لمدة دقيقة على الأقل، وسجّل ثلاثة أرقام: الطلبات في الثانية، وزمن الاستجابة عند المئين الخمسين (p50)، وعند المئين الخامس والتسعين (p95) لأنه يكشف الذيل البطيء الذي يشعر به المستخدمون فعلاً. قِس FPM أولاً كخط أساس، ثم Octane بنفس السيناريو تماماً على نفس العتاد. الفرق الحقيقي في p95 أهم من متوسط لامع، ورقم p95 المنخفض هو ما يجعل تطبيقك يبدو سريعاً تحت الضغط.
خاتمة عملية
قرّر بهذا التسلسل: أولاً فعّل OPcache وقِس بأداة مثل ab أو wrk، ثانياً حسّن أبطأ استعلاماتك بـEXPLAIN، ثالثاً أضف تخزيناً مؤقتاً للبيانات المتكررة. إن بقي معدل طلباتك دون حاجتك ورأيت أن الإقلاع يستهلك جزءاً كبيراً من زمن الاستجابة، جرّب Octane في بيئة اختبار أولاً، وراجع كل الخصائص الساكنة والـsingletons بحثاً عن حالة مقيمة قبل النشر. اضبط max_requests لإعادة تدوير العمّال دورياً كشبكة أمان ضد تسرّب الذاكرة، وإن أردت خادماً بصلاحية جذر كاملة تثبّت عليه Swoole وتضبط العمّال بحرية، ابدأ من باقة نمو على سيرفرات الرياض بزمن استجابة نحو 8ms.


