Laravel Octane: دليل شامل لتسريع تطبيقات Laravel باستخدام FrankenPHP وRoadRunner وSwoole

عندما يبدأ تطبيق Laravel بالنمو، قد تلاحظ أن جزءًا من زمن كل طلب HTTP لا يُستهلك في منطق التطبيق نفسه، وإنما في إعادة تشغيل إطار العمل وتهيئة الـ Service Container وتحميل مزودي الخدمات وإعداد المكونات اللازمة لمعالجة الطلب. قد تكون استعلامات قاعدة البيانات سريعة والكود محسّنًا، ومع ذلك يبقى هناك زمن ثابت يتكرر مع كل طلب. هنا تأتي Laravel Octane: بدل إعادة تشغيل Laravel من الصفر في كل مرة، يتم تشغيل التطبيق مرة واحدة، الاحتفاظ به في الذاكرة، ثم استخدام نفس التطبيق لمعالجة سلسلة من الطلبات عبر خوادم تطبيقات عالية الأداء.

لكن Octane ليس زرًا سحريًا يجعل أي تطبيق سريعًا. انتقال Laravel إلى نموذج طويل العمر Long-Lived Application يغيّر بعض الافتراضات التي اعتاد عليها مطورو PHP، خصوصًا في ما يتعلق بالـ Singletons والحالة المشتركة وحقن Request داخل الخدمات وتسرب الذاكرة. لذلك سنشرح في هذا المرجع كيف يعمل Laravel Octane، وكيف يتم تثبيته وتشغيله وإعداده للإنتاج، ومتى تحتاج إليه، وكيف تتجنب الأخطاء التي قد تظهر عند تحويل تطبيق تقليدي إلى تطبيق يعمل عبر Octane.

يعتمد هذا الشرح على توثيق Laravel 13.x الرسمي لـ Laravel Octane وتوثيق نشر تطبيقات Laravel في بيئة الإنتاج.

ما هو Laravel Octane؟

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

وفق التوثيق الرسمي، يقوم Octane بتشغيل Laravel مرة واحدة ثم يحتفظ بالتطبيق في الذاكرة ويغذيه بالطلبات اللاحقة. ويمكن تشغيله حاليًا باستخدام FrankenPHP أو RoadRunner أو Swoole أو Open Swoole.

الفكرة الأساسية في Octane ليست أن PHP أصبح أسرع فجأة، بل أنك تتجنب جزءًا كبيرًا من تكلفة Bootstrap المتكررة لتطبيق Laravel عند كل Request.

Octane لا يستبدل Laravel ولا يغير طريقة كتابة Routes أو Controllers أو Eloquent Models في معظم الحالات. الفرق الحقيقي موجود في دورة حياة التطبيق: العملية التي تخدم الطلب الحالي قد تستمر وتخدم عشرات أو مئات الطلبات التالية.

كيف يعمل Laravel Octane؟

في النموذج التقليدي لتطبيق PHP، يمكن تبسيط دورة الطلب بالشكل التالي:

  1. يصل طلب HTTP.
  2. يتم تشغيل التطبيق.
  3. يتم إنشاء Service Container.
  4. يتم تسجيل وتشغيل Service Providers.
  5. يتم تحميل الإعدادات والخدمات.
  6. يعالج Laravel الطلب.
  7. ترسل الاستجابة.
  8. تنتهي دورة التطبيق الخاصة بهذا الطلب.

أما مع Octane، فيتم تشغيل التطبيق وإجراء مرحلة الـ Bootstrap عند إنشاء Worker، ثم يبقى هذا العامل في الذاكرة ويعالج عدة Requests.

Worker starts
    |
    +-- Laravel boots
    |
    +-- Request 1
    |
    +-- Request 2
    |
    +-- Request 3
    |
    +-- ...
    |
    +-- Worker reload / restart

هذه المعمارية تقلل الأعمال المتكررة، لكنها تخلق قاعدة مهمة جدًا:

لا تفترض أن جميع البيانات الموجودة في ذاكرة PHP ستختفي بعد نهاية Request، لأن عملية Worker نفسها قد تستمر لمعالجة طلبات أخرى.

Laravel يعيد تعيين حالة العديد من مكونات Framework الرسمية بين الطلبات، لكن Laravel لا يستطيع معرفة جميع الحالات العالمية أو الثابتة التي ينشئها كود تطبيقك أو مكتبات خارجية.

متى تحتاج إلى Laravel Octane؟

قد يكون Octane مناسبًا عندما يكون لديك تطبيق Laravel يستقبل عددًا كبيرًا من الطلبات أو عندما يكون زمن استجابة التطبيق أمرًا مهمًا، مثل APIs وتطبيقات SaaS ولوحات التحكم والتطبيقات ذات الزيارات المرتفعة.

لكن قبل إضافة Octane يجب التفريق بين نوعين من مشاكل الأداء. إذا كان طلبك يستغرق ثانيتين بسبب استعلام SQL سيئ، فلن يقوم Octane بإصلاح الاستعلام. وإذا كانت الصفحة تنفذ عشرات الاتصالات البطيئة مع خدمات خارجية، فالمشكلة الأساسية ما تزال تلك العمليات.

Octane يكون أكثر فائدة عندما تريد تقليل تكلفة تشغيل Framework المتكررة والاستفادة من Workers طويلة العمر، بالإضافة إلى ميزات متقدمة يقدمها Swoole مثل Concurrent Tasks وTicks وOctane Cache وSwoole Tables.

الخوادم التي يدعمها Laravel Octane

يوفر Octane طبقة تكامل بين Laravel وعدة خوادم تطبيقات. الخادم الذي تختاره يؤثر في طريقة التثبيت وبعض الميزات المتاحة.

الخادمطريقة العمل العامةمتطلبات مهمةميزات Octane الخاصة
FrankenPHPPHP Application Server مكتوب بلغة Goيمكن لـ Octane تنزيل الـ Binary عند التثبيتيدعم تشغيل Laravel طويل العمر، إضافة إلى إمكانات حديثة يقدمها FrankenPHP
RoadRunnerApplication Server يعتمد Binary مكتوبًا بلغة Goيحتاج RoadRunner Binary والحزم المرتبطة بهتشغيل Workers طويلة العمر بكفاءة
SwoolePHP Extension وخادم عالي الأداءتثبيت امتداد SwooleConcurrent Tasks وTicks وIntervals وOctane Cache وTables
Open Swooleامتداد PHP متوافق مع تكامل Octaneتثبيت امتداد Open Swooleيوفر وظائف Swoole نفسها التي يوثقها Octane مثل Concurrent Tasks وTicks وIntervals

FrankenPHP

يصف توثيق Laravel الحالي FrankenPHP بأنه خادم تطبيقات PHP مكتوب بلغة Go ويدعم ميزات ويب حديثة مثل Early Hints وضغط Brotli وZstandard. عند اختيار FrankenPHP أثناء تثبيت Octane يستطيع Octane تنزيل الـ Binary وتثبيته.

RoadRunner

يعتمد RoadRunner أيضًا على Binary مكتوب بلغة Go. وعند بدء تشغيل Octane باستخدام RoadRunner للمرة الأولى، يمكن لـ Octane عرض تنزيل الـ Binary المطلوب.

Swoole وOpen Swoole

Swoole مختلف قليلًا لأنه يعتمد على امتداد PHP. ويمكن تثبيت Swoole عبر PECL:

pecl install swoole

أما Open Swoole:

pecl install openswoole

إذا كنت تحتاج ميزات Octane المتقدمة مثل Octane::concurrently() أو Ticks أو Octane Cache أو Tables، فانتبه إلى أن التوثيق الرسمي يربط هذه الوظائف بـ Swoole، ويذكر أن Open Swoole يوفر وظائف Swoole نفسها في هذا السياق.

تثبيت Laravel Octane

ابدأ بتثبيت الحزمة عبر Composer:

composer require laravel/octane

بعد ذلك شغّل أمر التثبيت:

php artisan octane:install

يقوم الأمر بتثبيت إعدادات Octane داخل المشروع وسيطلب منك اختيار Application Server المناسب.

يمكنك كذلك تحديد الخادم مباشرة في بعض سيناريوهات التثبيت، مثل FrankenPHP:

php artisan octane:install --server=frankenphp

بعد التثبيت ستجد إعدادات Octane الأساسية في:

config/octane.php

هذا الملف مهم لأنه يتحكم في خيارات مثل الخادم المستخدم، الملفات التي تتم مراقبتها، HTTPS، عدد الطلبات وبعض إعدادات Swoole والجداول.

تشغيل تطبيق Laravel باستخدام Octane

بعد الانتهاء من التثبيت يمكن تشغيل التطبيق باستخدام:

php artisan octane:start

يستخدم الأمر الخادم المحدد في إعداد server داخل config/octane.php. ويعمل Octane افتراضيًا على المنفذ 8000.

يمكنك كذلك تحديد الخادم يدويًا:

php artisan octane:start --server=frankenphp

أو:

php artisan octane:start --server=roadrunner

أو:

php artisan octane:start --server=swoole

معرفة حالة Octane

php artisan octane:status

إيقاف Octane

php artisan octane:stop

إعادة تحميل Workers

php artisan octane:reload

الأمر الأخير مهم جدًا أثناء النشر، لأن الكود القديم قد يكون ما يزال موجودًا داخل ذاكرة Workers حتى بعد استبدال ملفات المشروع.

التطوير ومراقبة تغييرات الملفات

بما أن Laravel يتم تحميله إلى الذاكرة عند تشغيل Octane، فإن تعديل ملف PHP لا يعني بالضرورة أن Worker الحالي سيقرأ النسخة الجديدة مباشرة.

لهذا يوفر Octane خيار:

php artisan octane:start --watch

يقوم هذا الخيار بإعادة تشغيل الخادم تلقائيًا عندما تتغير الملفات التي يراقبها Octane.

بحسب التوثيق الرسمي، تحتاج إلى Node.js بالإضافة إلى مكتبة Chokidar:

npm install --save-dev chokidar

يمكن التحكم في الملفات والمجلدات التي تتم مراقبتها من خلال خيار watch في config/octane.php.

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

Workers: قلب Laravel Octane

الـ Worker هو العملية التي تحمل Laravel في الذاكرة وتعالج الطلبات. افتراضيًا، يبدأ Octane عامل Requests واحدًا لكل CPU Core متوفر على الجهاز.

يمكنك تحديد عدد Workers يدويًا:

php artisan octane:start --workers=4

زيادة Workers ليست دائمًا أفضل. يجب أن تتناسب مع موارد الجهاز وطبيعة التطبيق واستهلاك الذاكرة وقاعدة البيانات والخدمات الخارجية.

إعادة تشغيل Worker بعد عدد من الطلبات

لحماية التطبيق من تسربات الذاكرة العرضية، يعيد Octane تشغيل Worker بصورة سلسة بعد أن يعالج 500 Request افتراضيًا.

يمكن تغيير العدد:

php artisan octane:start --max-requests=250

هذا لا يعني أن كل تطبيق يعاني Memory Leak، ولكنه طبقة وقائية مهمة في التطبيقات طويلة العمر.

الحد الأقصى لمدة تنفيذ Request

في Laravel Octane الحالي، القيمة الافتراضية لـ max_execution_time هي 30 ثانية:

'max_execution_time' => 30,

ويحدد هذا الخيار المدة القصوى التي يمكن أن يستغرقها Request قبل إنهائه.

إذا ضبطته على صفر:

'max_execution_time' => 0,

سيتم تعطيل هذا الحد.

قد تحتاج إلى تعديل القيمة للطلبات طويلة التنفيذ، مثل بعض عمليات رفع الملفات أو معالجة البيانات أو الاتصال بخدمات خارجية، لكن تعطيل الحد بالكامل يجب أن يكون قرارًا مدروسًا حتى لا تحتجز Requests معلقة موارد Workers إلى أجل غير محدود.

بعد تعديل القيمة يجب إعادة تشغيل Octane حتى تدخل الإعدادات الجديدة حيز التنفيذ.

Dependency Injection: أهم شيء يجب فهمه قبل استخدام Octane

أكبر فرق ذهني بين Laravel التقليدي وOctane هو أن Service Provider لا يعاد تشغيله بالضرورة لكل طلب. توضح وثائق Laravel أن register وboot في Service Providers يتم تنفيذهما عندما يبدأ Worker، ثم يعاد استخدام التطبيق في الطلبات اللاحقة.

لذلك يجب الحذر من وضع كائنات مرتبطة بالطلب الحالي داخل Singleton طويل العمر.

مثال قد يسبب مشكلة

use App\Services\ReportService;
use Illuminate\Contracts\Foundation\Application;

public function register(): void
{
    $this->app->singleton(ReportService::class, function (Application $app) {
        return new ReportService($app);
    });
}

إذا تم إنشاء هذا Singleton مبكرًا، فقد يحتفظ بمرجع إلى Container قديم بدل الحصول على الحالة الحالية المطلوبة في Request لاحق.

الحل الأول: لا تستخدم Singleton إذا لم تكن بحاجة إليه

$this->app->bind(ReportService::class, function (Application $app) {
    return new ReportService($app);
});

الحل الثاني: استخدم Resolver للحصول على Container الحالي

use Illuminate\Container\Container;

$this->app->singleton(ReportService::class, function () {
    return new ReportService(
        fn () => Container::getInstance()
    );
});

مشكلة حقن Request

الفكرة نفسها تنطبق على Illuminate\Http\Request. إذا احتفظ Singleton بكائن Request الذي كان موجودًا عند إنشاء الخدمة، فقد تستخدم الخدمة في Request جديد بيانات الطلب القديم.

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

في Octane، اسأل دائمًا: هل هذا الكائن سيعيش فقط أثناء Request الحالي، أم يمكن أن يبقى في الذاكرة ويستخدم مرة أخرى؟

إدارة Memory Leaks في Laravel Octane

في PHP التقليدي قد يمر خطأ صغير في إدارة الحالة دون أن تلاحظه لأن العملية تنتهي بعد Request. أما في Octane، فإن إضافة بيانات باستمرار إلى متغير Static قد تجعل استهلاك الذاكرة يزداد مع كل Request.

مثال سيئ

class MetricsStore
{
    public static array $requests = [];
}

public function index()
{
    MetricsStore::$requests[] = request()->path();

    return response()->json([
        'status' => 'ok',
    ]);
}

في هذا المثال تستمر المصفوفة في النمو طالما بقي Worker نفسه حيًا.

ولهذا توصي وثائق Laravel بمراقبة استهلاك الذاكرة أثناء التطوير والانتباه إلى البيانات المخزنة في الخصائص Static أو المتغيرات العالمية أو Singletons المخصصة.

ما الذي يجب تجنبه؟

  • مصفوفات Static يضاف إليها محتوى عند كل Request دون تنظيفها.
  • Singletons تحتفظ ببيانات خاصة بمستخدم أو Request.
  • تخزين Request الحالي داخل Property طويلة العمر.
  • إنشاء Cache داخل الذاكرة يدويًا دون سياسة واضحة لحجمه.
  • الاعتماد على أن نهاية Request ستزيل جميع المتغيرات من العملية.

كما يوفر --max-requests طبقة حماية إضافية لأنه يعيد تدوير Workers دوريًا.

Concurrent Tasks: تنفيذ عمليات بالتوازي

هذه الميزة تتطلب Swoole. يمكن من خلالها تنفيذ عدة عمليات بصورة متزامنة باستخدام:

use App\Models\Server;
use App\Models\User;
use Laravel\Octane\Facades\Octane;

[$users, $servers] = Octane::concurrently([
    fn () => User::all(),
    fn () => Server::all(),
]);

تُنفذ هذه المهام داخل Task Workers منفصلة عن العملية التي تعالج Request الحالي.

يمكن تحديد عدد Task Workers:

php artisan octane:start --workers=4 --task-workers=6

يحدد التوثيق الرسمي حدًا مهمًا: لا ينبغي تمرير أكثر من 1024 مهمة إلى Octane::concurrently() بسبب حدود نظام المهام في Swoole.

Concurrent Tasks مفيدة عندما تكون لديك عمليات مستقلة يمكن تنفيذها في الوقت نفسه. لكنها ليست بديلًا تلقائيًا عن Laravel Queues؛ فالعملية التي يجب تنفيذها لاحقًا أو خارج دورة HTTP غالبًا تنتمي إلى Queue، بينما concurrently يهدف إلى إنجاز عمليات متزامنة ضمن سيناريو التنفيذ الحالي.

Ticks وIntervals

هذه الميزة تتطلب Swoole أيضًا. تسمح Ticks بتنفيذ Callback كل عدد محدد من الثواني.

use Laravel\Octane\Facades\Octane;

Octane::tick('refresh-statistics', function () {
    // Refresh lightweight in-memory data...
})->seconds(10);

وعادة يتم تسجيل Tick داخل boot لأحد Service Providers.

إذا أردت تشغيل Callback فور بدء الخادم ثم تكراره:

Octane::tick('refresh-statistics', function () {
    // ...
})
->seconds(10)
->immediate();

استخدم هذه الميزة للأعمال الخفيفة والدورية المناسبة لطبيعة Worker طويل العمر. ولا تعتبرها بديلًا عامًا عن Laravel Scheduler عندما يكون المطلوب جدولة أعمال موثوقة ومستمرة على مستوى البنية التحتية.

Octane Cache

عند استخدام Swoole يوفر Octane Cache Driver سريعًا قائمًا على Swoole Tables. وتشير وثائق Laravel إلى أنه قادر على الوصول إلى معدلات قراءة وكتابة تصل إلى نحو مليوني عملية في الثانية.

use Illuminate\Support\Facades\Cache;

Cache::store('octane')->put('framework', 'Laravel', 30);

$value = Cache::store('octane')->get('framework');

تكون البيانات متاحة لجميع Workers الموجودة على الخادم، لكن هناك فرقًا بالغ الأهمية مقارنة بـ Redis أو قواعد البيانات:

البيانات الموجودة في Octane Cache تُفقد عند إعادة تشغيل الخادم.

لذلك لا تستخدمه لتخزين بيانات دائمة أو معلومات لا يمكن إعادة بنائها.

Interval Cache

يدعم Octane كذلك قيم Cache يتم تحديثها دوريًا:

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;

Cache::store('octane')->interval('random', function () {
    return Str::random(10);
}, seconds: 5);

هذه التقنية مناسبة للبيانات التي يتم قراءتها بكثافة ويمكن إعادة حسابها دوريًا.

Swoole Tables

يمكن عند استخدام Swoole تعريف جداول مشتركة بين Workers. ويتم تعريفها في config/octane.php.

'tables' => [
    'metrics:1000' => [
        'name' => 'string:1000',
        'requests' => 'int',
        'load' => 'float',
    ],
],

ثم التعامل معها:

use Laravel\Octane\Facades\Octane;

Octane::table('metrics')->set('api', [
    'name' => 'API',
    'requests' => 1200,
    'load' => 0.82,
]);

$data = Octane::table('metrics')->get('api');

الأنواع التي يدعمها Swoole Table بحسب توثيق Octane هي:

  • string
  • int
  • float

وكما هو الحال مع Octane Cache، هذه البيانات موجودة في الذاكرة وتفقد عند إعادة تشغيل الخادم.

إعداد Laravel Octane في بيئة الإنتاج

لا يكفي تشغيل:

php artisan octane:start

داخل جلسة SSH وتركها مفتوحة. توصي Laravel باستخدام Process Monitor مثل Supervisor لضمان إعادة تشغيل Octane إذا توقفت العملية.

يعرض التوثيق الرسمي مثالًا مشابهًا للتالي:

[program:octane]
process_name=%(program_name)s_%(process_num)02d
command=php /home/forge/example.com/artisan octane:start --server=frankenphp --host=127.0.0.1 --port=8000
autostart=true
autorestart=true
user=forge
redirect_stderr=true
stdout_logfile=/home/forge/example.com/storage/logs/octane.log
stopwaitsecs=3600

الفكرة ليست اسم المستخدم أو المسار حرفيًا، بل ضمان وجود مدير عمليات مسؤول عن تشغيل Octane ومراقبته وإعادته إلى العمل عند الحاجة.

HTTPS وNginx مع Laravel Octane

في الإنتاج توصي وثائق Laravel بوضع Octane خلف Web Server تقليدي مثل Nginx أو Apache. يتولى Web Server تقديم الملفات الثابتة وإدارة SSL Termination ثم يمرر Requests الديناميكية إلى Octane.

OCTANE_HTTPS

إذا كان التطبيق يقدم للمستخدمين عبر HTTPS، يمكن استخدام:

OCTANE_HTTPS=true

ويرتبط ذلك بالخيار التالي في config/octane.php:

'https' => env('OCTANE_HTTPS', false),

يساعد ذلك Laravel على إنشاء الروابط باستخدام https://.

نمط Nginx الموصى به

يستخدم المثال الرسمي Nginx لتقديم Static Assets ثم Proxy الطلبات إلى Octane على 127.0.0.1:8000.

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    listen [::]:80;

    server_name domain.com;
    server_tokens off;

    root /home/forge/domain.com/public;
    index index.php;

    charset utf-8;

    location /index.php {
        try_files /not_exists @octane;
    }

    location / {
        try_files $uri $uri/ @octane;
    }

    location @octane {
        proxy_http_version 1.1;

        proxy_set_header Host $http_host;
        proxy_set_header Scheme $scheme;
        proxy_set_header SERVER_PORT $server_port;
        proxy_set_header REMOTE_ADDR $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        proxy_pass http://127.0.0.1:8000;
    }
}

يمكن أن يحتاج إعدادك الفعلي إلى تعديلات حسب البنية التحتية، لكن المبدأ المهم هو فصل واجهة HTTP العامة عن عملية Octane الداخلية.

نشر إصدار جديد من تطبيق يعمل بـ Octane

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

لأن التطبيق موجود في الذاكرة، يجب إعادة تحميل Workers بعد Deployment:

php artisan octane:reload

كما توفر Laravel الحديثة أمرًا عامًا لإعادة تحميل الخدمات طويلة العمر بعد Deployment:

php artisan reload

ويشمل مفهوم الخدمات طويلة العمر عمليات مثل Laravel Octane وQueue Workers وLaravel Reverb. يجب أن يكون لديك Process Monitor يعيد تشغيل العمليات عندما تخرج.

مثال تسلسل نشر مبسط

composer install --no-dev --optimize-autoloader

php artisan migrate --force

php artisan optimize

php artisan octane:reload

حدد خطوات Deployment الفعلية حسب مشروعك، لكن النقطة الأساسية هي أن نشر ملفات PHP وحده لا يكفي مع Worker طويل العمر.

اعتبارات الأمان عند استخدام Laravel Octane

Octane لا يغير قواعد أمان Laravel الأساسية، لكنه يضيف اعتبارات خاصة بسبب بقاء التطبيق في الذاكرة.

1. لا تسمح بتسرب بيانات Request بين المستخدمين

أخطر خطأ منطقي يمكن إدخاله إلى تطبيق Octane هو تخزين بيانات Request أو User داخل Singleton أو Static Property ثم استخدامها في طلب آخر.

تجنب أن يصبح كائن طويل العمر مخزنًا لبيانات مستخدم سابق.

2. ضع Octane خلف Web Server في الإنتاج

التوثيق الرسمي يوصي بتشغيل Octane خلف Nginx أو Apache، حيث يتم التعامل مع الملفات الثابتة وSSL أمام Octane.

3. عطّل Debug Mode في الإنتاج

وفق توثيق Laravel الخاص بالنشر، يتحكم APP_DEBUG في مقدار معلومات الخطأ المعروضة للمستخدم. في الإنتاج يجب ألا تعرض تفاصيل الاستثناءات والمعلومات الداخلية للزوار.

APP_ENV=production
APP_DEBUG=false

4. أعد تحميل Workers بعد تغيير الأسرار أو الإعدادات

بما أن التطبيق طويل العمر، فإن تغيير Config أو Environment أثناء وجود Workers قد لا ينعكس على العمليات الموجودة بالفعل. لذلك اجعل Restart أو Reload جزءًا من عملية Deployment.

5. لا تستخدم الذاكرة المشتركة كمخزن دائم

Octane Cache وSwoole Tables سريعتان للغاية، لكن محتواهما يفقد عند Restart. البيانات المهمة يجب أن تظل في مخزن مناسب ودائم.

كيف تحصل على أفضل أداء من Laravel Octane؟

1. أصلح الاختناقات قبل Octane

ابدأ بقياس Queries، واستعلامات N+1، والاتصالات الخارجية، وعمليات Cache، وحجم Responses. Octane يقلل تكلفة تشغيل Framework لكنه لا يحل جميع مشاكل التطبيق.

2. اضبط عدد Workers بناءً على القياس

القيمة الافتراضية عامل لكل CPU Core نقطة بداية جيدة، لكن التطبيق الذي يستهلك ذاكرة كبيرة أو يعتمد بكثافة على قواعد البيانات قد يحتاج إعدادًا مختلفًا.

3. احتفظ بإعادة تدوير Workers

لا ترفع --max-requests إلى أرقام ضخمة دون سبب. إعادة تدوير Workers تساعد في احتواء Memory Leaks غير المقصودة.

4. راقب الذاكرة وليس CPU فقط

في تطبيق طويل العمر، استقرار Memory Usage مع مرور آلاف Requests أهم من اختبار سرعة قصير.

5. استخدم php artisan optimize للإنتاج

يوصي دليل Deployment الرسمي باستخدام تحسينات Laravel المناسبة للإنتاج، بما يشمل Cache للإعدادات والأحداث والـ Routes والـ Views حيث ينطبق.

6. لا تستخدم Octane Cache للشيء الخطأ

إذا كنت تحتاج Cache موزعًا بين عدة خوادم أو بيانات تستمر بعد Restart، استخدم Cache Backend مناسبًا لتلك الحاجة. Octane Cache هو ذاكرة محلية شديدة السرعة، وليس بديلًا عالميًا لكل أنواع Cache.

مثال عملي: API تجمع بيانات من أكثر من مصدر

لنفترض أن لديك Endpoint يعرض Dashboard ويحتاج بيانات المستخدمين والخوادم في الوقت نفسه.

النسخة العادية قد تكون:

public function index()
{
    $users = User::query()
        ->latest()
        ->limit(10)
        ->get();

    $servers = Server::query()
        ->latest()
        ->limit(10)
        ->get();

    return response()->json([
        'users' => $users,
        'servers' => $servers,
    ]);
}

مع Swoole يمكن تشغيل العمليتين بصورة متزامنة:

use App\Models\Server;
use App\Models\User;
use Laravel\Octane\Facades\Octane;

public function index()
{
    [$users, $servers] = Octane::concurrently([
        fn () => User::query()
            ->latest()
            ->limit(10)
            ->get(),

        fn () => Server::query()
            ->latest()
            ->limit(10)
            ->get(),
    ]);

    return response()->json([
        'users' => $users,
        'servers' => $servers,
    ]);
}

لكن لا تفترض تلقائيًا أن كل عملية متزامنة ستكون أسرع. يجب Benchmark السيناريو الفعلي، لأن قاعدة البيانات وعدد Connections وطبيعة العمليات قد تغير النتيجة.

Laravel Octane مقارنة بحزم وخدمات Laravel الأخرى

من الأخطاء المفاهيمية وضع Octane وHorizon وReverb في الفئة نفسها. جميعها خدمات طويلة العمر في Laravel، لكنها تعالج مشكلات مختلفة.

الأداةوظيفتهاهل تستبدل Octane؟
Laravel Octaneتشغيل تطبيق Laravel HTTP باستخدام Workers طويلة العمر وخوادم عالية الأداء—
Laravel Horizonإدارة ومراقبة Laravel Queues المبنية على Redisلا
Laravel Reverbخادم WebSocket رسمي لتطبيقات Laravel وBroadcastingلا
Laravel Queue Workersتنفيذ Jobs خارج دورة HTTPلا

يمكن أن يعمل تطبيق واحد باستخدام Octane لمعالجة HTTP Requests، وHorizon لمعالجة Queued Jobs، وReverb لاتصالات WebSocket في الوقت نفسه.

Octane أم PHP-FPM؟

Octane يغير نموذج تنفيذ التطبيق عبر إبقائه في الذاكرة بين Requests، بينما النموذج التقليدي لا يفترض بقاء Application State بالطريقة نفسها. نتيجة ذلك أن Octane يمكنه إزالة جزء من تكلفة Bootstrap المتكررة، لكنه يفرض عليك الانتباه إلى الحالة طويلة العمر وMemory Leaks.

أخطاء شائعة عند استخدام Laravel Octane

الخطأ 1: نسيان Reload بعد Deployment

قد ترى ملفات جديدة على الخادم بينما المستخدم ما يزال يحصل على سلوك النسخة السابقة، لأن Worker لم تتم إعادة تحميله.

php artisan octane:reload

الخطأ 2: تخزين Request داخل Singleton

قد يؤدي ذلك إلى استخدام بيانات Request قديم في Request جديد.

الخطأ 3: استخدام Static Arrays كـ Cache

إذا استمرت المصفوفة بالنمو فقد ينمو استهلاك الذاكرة طوال عمر Worker.

الخطأ 4: توقع أن --watch يعمل دون Chokidar

تحتاج ميزة Watch إلى Node وChokidar في بيئة التطوير.

الخطأ 5: الاعتقاد أن Concurrent Tasks متاحة لكل Server

توثيق Octane يحدد هذه الخاصية ضمن وظائف Swoole.

الخطأ 6: اعتبار Octane Cache مخزنًا دائمًا

محتواه يفقد عند Restart للخادم.

الخطأ 7: زيادة Workers بلا قياس

Workers أكثر تعني عمليات أكثر وذاكرة واتصالات محتملة أكثر. حدد العدد بالاختبار وليس بالتخمين.

الخطأ 8: استخدام Octane لعلاج SQL بطيء

Octane ليس بديلًا عن Indexes المناسبة وتحسين Queries وEager Loading وتحليل الاختناقات.

أفضل الممارسات مع Laravel Octane

  • ابدأ بتطبيق يعمل بصورة صحيحة ومستقرة قبل الانتقال إلى Octane.
  • راقب Memory Usage على مدى عدد كبير من Requests وليس في اختبار واحد.
  • تجنب Global State وStatic State المتنامية.
  • لا تربط بيانات Request بعمر Singleton.
  • استخدم --max-requests كطبقة حماية من Memory Leaks العرضية.
  • اضبط Workers اعتمادًا على CPU والذاكرة وطبيعة التطبيق.
  • اجعل octane:reload أو آلية Reload المناسبة جزءًا من Deployment.
  • استخدم Process Monitor في الإنتاج.
  • ضع Octane خلف Web Server مناسب في الإنتاج عندما تكون مسؤولًا عن الخادم.
  • استخدم HTTPS بصورة صحيحة واضبط OCTANE_HTTPS عند الحاجة.
  • لا تعرض APP_DEBUG=true في الإنتاج.
  • استخدم --watch للتطوير بدل إعادة التشغيل اليدوي المستمر.
  • لا تستخدم ميزات Swoole الخاصة وأنت تفترض أنها متاحة تلقائيًا على كل Server.
  • نفذ Benchmark قبل وبعد Octane بدل الاعتماد على أرقام عامة.
  • اختبر جميع المكتبات الخارجية المهمة تحت نموذج Worker طويل العمر.

الأسئلة الشائعة حول Laravel Octane

هل Laravel Octane ضروري لكل مشروع Laravel؟

لا. التطبيقات الصغيرة أو التطبيقات التي لا تعاني من ضغط كبير قد تعمل بصورة ممتازة باستخدام البنية التقليدية. Octane يصبح أكثر أهمية عندما يمثل Latency أو Throughput عاملًا مهمًا.

هل Octane يجعل استعلامات MySQL أسرع؟

لا يغير Octane سرعة الاستعلام نفسه. فائدته الأساسية في نموذج تشغيل Laravel وتقليل الأعمال المتكررة حول معالجة Request. يجب تحسين قواعد البيانات بصورة مستقلة.

ما الخوادم التي يدعمها Octane حاليًا؟

يوثق Laravel 13.x حاليًا FrankenPHP وRoadRunner وSwoole وOpen Swoole.

كم Worker يشغل Octane افتراضيًا؟

يبدأ Octane افتراضيًا Application Request Worker لكل CPU Core متوفر في الجهاز.

بعد كم Request يعاد تشغيل Worker؟

القيمة الافتراضية الحالية هي 500 Request لكل Worker، ويمكن تعديلها باستخدام --max-requests.

ما قيمة Max Execution Time الافتراضية؟

القيمة الافتراضية الحالية هي 30 ثانية ويمكن تغييرها من config/octane.php. والقيمة صفر تعطل الحد.

لماذا لا تظهر تغييرات الكود أثناء التطوير؟

لأن التطبيق موجود في ذاكرة Worker. استخدم:

php artisan octane:start --watch

مع Node وChokidar، أو أعد تشغيل الخادم يدويًا.

هل Octane Cache بديل عن Redis؟

ليس بصورة عامة. Octane Cache مع Swoole سريع جدًا لكنه موجود في ذاكرة الخادم ويفقد عند Restart، بينما قد تحتاج Redis عندما تريد Cache دائمًا نسبيًا أو مشتركًا بين أكثر من خادم.

هل يمكن استخدام Laravel Horizon مع Octane؟

نعم من ناحية الأدوار؛ الأداتان تحلان مشكلتين مختلفتين. Octane يعالج تشغيل تطبيق HTTP، بينما Horizon مخصص لإدارة ومراقبة Redis Queues.

هل يمكن استخدام Reverb مع Octane؟

هما خدمتان مختلفتان. Reverb مخصص لاتصالات WebSocket وLaravel Broadcasting، بينما Octane يخدم تطبيق Laravel عبر HTTP باستخدام نموذج Workers طويلة العمر.

هل أحتاج Supervisor؟

إذا كنت تدير بيئة الإنتاج بنفسك، يوصي توثيق Octane باستخدام Process Monitor مثل Supervisor لإبقاء العملية قيد التشغيل وإعادة تشغيلها عند الحاجة.

هل يجب إعادة تشغيل Octane بعد كل Deployment؟

يجب إعادة تحميل أو إعادة تشغيل الخدمة الطويلة العمر حتى تستخدم Workers الكود الجديد. يوفر Octane الأمر octane:reload، كما توفر Laravel الحديثة أمر php artisan reload لإعادة تحميل الخدمات طويلة العمر.

الخلاصة

Laravel Octane ليس مجرد أداة Benchmark للحصول على رقم أكبر في Requests per Second، بل تغيير في طريقة تشغيل تطبيق Laravel. بدلاً من إنشاء التطبيق وإزالته في كل Request، يتم تشغيل Workers تحمل Framework في الذاكرة وتستمر في استقبال الطلبات.

هذا النموذج يمكن أن يخفض زمن الاستجابة ويرفع قدرة التطبيق على معالجة الطلبات، لكنه يتطلب فهمًا جيدًا لدورة حياة الكائنات. أهم ما يجب إتقانه قبل استخدام Octane في الإنتاج هو معرفة الفرق بين البيانات الخاصة بـ Request والبيانات التي يمكن أن تبقى طوال عمر Worker.

إذا التزمت بعدم الاحتفاظ بحالة Request داخل Singletons، وراقبت Memory Leaks، وضبطت Workers وmax-requests بصورة مناسبة، واستخدمت Process Monitor وReload أثناء Deployment، فإن Octane يصبح إحدى أقوى الأدوات الرسمية المتاحة لبناء تطبيقات Laravel عالية الأداء.

وإذا اخترت Swoole أو Open Swoole، تحصل فوق ذلك على أدوات متقدمة مثل Concurrent Tasks وTicks وOctane Cache وSwoole Tables، وهي ميزات يمكن أن تفتح نماذج أداء لا يوفرها تشغيل Laravel التقليدي.

المراجع الرسمية

هذا المقال جزء من سلسلة حزم Laravel الرسمية، ويشرح Laravel Octane وفق توثيق Laravel 13.x الرسمي.