فصل ۸: نظارت و بهینه‌سازی عملکرد

در این فصل، به بررسی ابزارها و تکنیک‌های نظارت و بهینه‌سازی عملکرد Laravel Reverb در نسخه ۱۲ لاراول می‌پردازیم. موضوعات شامل ادغام با Laravel Pulse برای نظارت بر اتصالات و پیام‌ها، تنظیم recorders برای جمع‌آوری داده‌ها، بهینه‌سازی event loop با استفاده از ReactPHP و ext-uv برای مدیریت اتصالات بالا، و مدیریت حافظه و garbage collection در سرور Reverb است. این فصل بر اساس مستندات رسمی لاراول ۱۲.x و بهترین شیوه‌های توسعه تنظیم شده و شامل مثال‌های عملی است.

ادغام با Laravel Pulse برای نظارت بر اتصالات و پیام‌ها

Laravel Pulse یک ابزار نظارت سبک و بومی است که برای مانیتورینگ عملکرد اپلیکیشن‌های لاراول طراحی شده است. با استفاده از Pulse، می‌توانید اتصالات WebSocket، پیام‌های ارسالی، و معیارهای عملکرد Reverb را در یک داشبورد بصری مشاهده کنید.

نصب و تنظیم Laravel Pulse

  1. نصب Pulse:
   composer require laravel/pulse
   php artisan vendor:publish --provider="Laravel\Pulse\PulseServiceProvider"
   php artisan migrate
  1. اجرای Pulse: Pulse به‌صورت پیش‌فرض داده‌ها را در دیتابیس ذخیره می‌کند. برای فعال کردن داشبورد:
   php artisan pulse:install

این دستور route‌های لازم را به پروژه اضافه می‌کند و داشبورد در آدرس /pulse قابل دسترسی خواهد بود.

  1. تنظیم دسترسی به داشبورد: برای محدود کردن دسترسی به داشبورد Pulse، فایل config/pulse.php را ویرایش کنید:
<?php

return [
    'dashboard' => [
        'enabled' => true,
        'middleware' => ['auth', 'role:admin'],
    ],
    'storage' => [
        'driver' => 'database',
        'connection' => 'mysql',
    ],
];
  • middleware: دسترسی را به کاربران با نقش admin محدود می‌کند.
  • storage: داده‌ها را در دیتابیس ذخیره می‌کند (می‌توانید از Redis نیز استفاده کنید).

  • فعال کردن Pulse برای Reverb:

Pulse به‌صورت خودکار معیارهای مربوط به Reverb را جمع‌آوری می‌کند، اما باید مطمئن شوید که Reverb به درستی تنظیم شده است (فایل config/reverb.php).

تنظیم Recorders (ReverbConnections, ReverbMessages) و داشبورد Pulse

Pulse از recorderها برای جمع‌آوری داده‌های خاص استفاده می‌کند. دو recorder مهم برای Reverb عبارتند از:

  • ReverbConnections: تعداد اتصالات WebSocket فعال را ردیابی می‌کند.
  • ReverbMessages: پیام‌های ارسالی از طریق Reverb را مانیتور می‌کند.

تنظیم Recorders

  1. فایل config/pulse.php را برای فعال کردن recorderهای Reverb ویرایش کنید:
<?php

return [
    'recorders' => [
        \Laravel\Pulse\Recorders\ReverbConnections::class => [
            'enabled' => true,
            'sample_rate' => 1.0, // جمع‌آوری 100% داده‌ها
        ],
        \Laravel\Pulse\Recorders\ReverbMessages::class => [
            'enabled' => true,
            'sample_rate' => 0.1, // جمع‌آوری 10% پیام‌ها
        ],
    ],
];
  • sample_rate: درصد داده‌های جمع‌آوری‌شده را مشخص می‌کند. برای محیط توسعه، می‌توانید از 1.0 استفاده کنید، اما در تولید، مقادیر پایین‌تر (مانند 0.1) برای کاهش بار توصیه می‌شود.

  • مشاهده داده‌ها در داشبورد Pulse:

پس از تنظیم، به آدرس /pulse بروید. در داشبورد، کارت‌های زیر نمایش داده می‌شوند:

  • Connections: تعداد کل اتصالات فعال WebSocket.
  • Messages: تعداد پیام‌های ارسالی در بازه‌های زمانی مختلف (مثلاً هر دقیقه).

مثال عملی: نمایش اتصالات و پیام‌ها

برای تست، یک event ساده ایجاد کنید که پیام‌ها را به یک کانال حضور ارسال کند:

<?php

namespace App\Events;

use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class PulseTestMessage implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $message;

    public function __construct($message)
    {
        $this->message = $message;
    }

    public function broadcastOn()
    {
        return new PresenceChannel('test-room');
    }

    public function broadcastAs()
    {
        return 'test.message';
    }
}

Route برای ارسال پیام:

<?php

use App\Events\PulseTestMessage;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('pulse-test');
});

Route::post('/send-test', function () {
    event(new PulseTestMessage(request('message', 'Test message for Pulse')));
    return response()->json(['status' => 'Message sent']);
});

فایل resources/views/pulse-test.blade.php:

<!DOCTYPE html>
<html>
<head>
    <title>Pulse Test</title>
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    @vite(['resources/js/app.js'])
</head>
<body>
    <h1>Test Reverb with Pulse</h1>
    <form id="test-form">
        <input type="text" name="message" placeholder="Type a message...">
        <button type="submit">Send</button>
    </form>
    <ul id="messages"></ul>

    <script>
        $.ajaxSetup({
            headers: {
                'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
            }
        });

        $('#test-form').submit(function(e) {
            e.preventDefault();
            $.post('/send-test', $(this).serialize());
        });

        window.Echo.join('test-room')
            .listen('.test.message', (e) => {
                $('#messages').append(`<li>${e.message}</li>`);
            });
    </script>
</body>
</html>
  1. سرور Reverb را اجرا کنید: php artisan reverb:start
  2. سرور لاراول را اجرا کنید: php artisan serve
  3. فایل‌های جاوااسکریپت را کامپایل کنید: npm run dev
  4. به /pulse بروید تا معیارهای اتصالات و پیام‌ها را مشاهده کنید.

بهینه‌سازی Event Loop با ReactPHP و ext-uv

Reverb از ReactPHP برای مدیریت event loop استفاده می‌کند، که برای پردازش اتصالات WebSocket به‌صورت غیرهمزمان مناسب است. برای سناریوهای با تعداد اتصالات بالا، می‌توانید از افزونه ext-uv برای بهبود عملکرد استفاده کنید.

نصب ext-uv

  1. افزونه ext-uv را نصب کنید (نیاز به PHP 8.2+ و سیستم لینوکس/مک):
   pecl install uv
  1. فایل php.ini را ویرایش کنید و افزونه را فعال کنید:
   extension=uv.so
  1. Reverb را برای استفاده از ext-uv تنظیم کنید:

فایل config/reverb.php:

<?php

return [
    'apps' => [
        [
            'app_id' => env('REVERB_APP_ID', 'my-reverb-app'),
            'key' => env('REVERB_APP_KEY', 'your-app-key'),
            'secret' => env('REVERB_APP_SECRET', 'your-app-secret'),
            'capacity' => 1000,
            'allowed_origins' => ['example.com'],
        ],
    ],
    'host' => env('REVERB_HOST', 'localhost'),
    'port' => env('REVERB_PORT', 8080),
    'scheme' => env('REVERB_SCHEME', 'http'),
    'event_loop' => 'uv', // استفاده از ext-uv
];
  • event_loop: با تنظیم روی uv، Reverb از افزونه ext-uv برای مدیریت event loop استفاده می‌کند، که عملکرد بهتری در مقایسه با event loop پیش‌فرض ReactPHP ارائه می‌دهد.

مزایای ext-uv

  • کارایی بالاتر: استفاده از libuv برای مدیریت I/O غیرهمزمان، که در Node.js نیز استفاده می‌شود.
  • مقیاس‌پذیری: مناسب برای اپلیکیشن‌هایی با هزاران اتصال همزمان.
  • کاهش تأخیر: بهبود زمان پاسخگویی برای پیام‌های WebSocket.

تست عملکرد

برای تست عملکرد با تعداد اتصالات بالا، از ابزارهایی مانند wrk یا k6 استفاده کنید:

k6 run -u 1000 -d 30s script.js

فایل script.js برای تست:

import ws from 'k6/ws';

export default function () {
    const url = 'ws://localhost:8080/app/your-app-key?protocol=7&client=js';
    const params = { tags: { name: 'reverb-test' } };

    const res = ws.connect(url, params, function (socket) {
        socket.on('open', () => {
            socket.send(JSON.stringify({
                command: 'subscribe',
                identifier: JSON.stringify({ channel: 'test-room' })
            }));
        });
        socket.on('message', (data) => console.log('Message received:', data));
        socket.setTimeout(() => socket.close(), 5000);
    });
}

مدیریت حافظه و Garbage Collection در سرور Reverb

برای اپلیکیشن‌های با اتصالات بالا، مدیریت حافظه و garbage collection حیاتی است تا از نشت حافظه (memory leaks) و کاهش عملکرد جلوگیری شود.

تکنیک‌های مدیریت حافظه

  1. محدود کردن اتصالات: همان‌طور که در فصل هفتم توضیح داده شد، از capacity در config/reverb.php برای محدود کردن تعداد اتصالات استفاده کنید.
  2. استفاده از Queues: برای کاهش بار روی سرور، broadcasting را به queue منتقل کنید:
BROADCAST_QUEUE_CONNECTION=redis
  1. تنظیم Garbage Collection: در PHP، garbage collection را بهینه کنید:
gc_enable();
ini_set('memory_limit', '512M'); // تنظیم در php.ini یا runtime
  1. مانیتورینگ حافظه:

از Pulse برای نظارت بر مصرف حافظه استفاده کنید. همچنین، می‌توانید از ابزارهای خارجی مانند New Relic یا Prometheus استفاده کنید.

مثال: لاگ‌گیری مصرف حافظه

برای ردیابی مصرف حافظه، یک middleware سفارشی ایجاد کنید:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Log;

class LogMemoryUsage
{
    public function handle($request, Closure $next)
    {
        $response = $next($request);
        Log::info('Memory usage', [
            'peak' => memory_get_peak_usage(true) / 1024 / 1024 . ' MB',
            'current' => memory_get_usage(true) / 1024 / 1024 . ' MB',
        ]);
        return $response;
    }
}

این middleware را به route‌های ارسال پیام اعمال کنید:

Route::middleware(['auth', 'log.memory'])->post('/send-test', function () {
    event(new PulseTestMessage(request('message', 'Test message')));
    return response()->json(['status' => 'Message sent']);
});

نکات و بهترین شیوه‌ها

  • نظارت مداوم: از Pulse یا ابزارهای خارجی مانند Prometheus برای مانیتورینگ real-time استفاده کنید.
  • بهینه‌سازی نمونه‌برداری: در Pulse، از sample_rate پایین‌تر در تولید استفاده کنید تا بار سرور کاهش یابد.
  • تست بار: قبل از استقرار، تست‌های بار (load testing) با ابزارهایی مانند k6 انجام دهید.
  • مدیریت منابع: در سرورهای با منابع محدود، از ext-uv و Redis برای بهبود عملکرد استفاده کنید.

تمرین پیشنهادی

  • یک کارت سفارشی در داشبورد Pulse برای نمایش تعداد پیام‌های ارسالی در هر کانال اضافه کنید.
  • یک تست بار با 5000 اتصال همزمان اجرا کنید و تأثیر ext-uv را در مقایسه با event loop پیش‌فرض بررسی کنید.
  • یک سیستم هشدار برای مصرف حافظه بالا پیاده‌سازی کنید (مانند ارسال ایمیل هنگام عبور از آستانه).

منابع

  • مستندات رسمی لاراول: laravel.com/docs/12.x/pulse
  • مستندات Reverb: laravel.com/docs/12.x/reverb
  • مستندات ReactPHP: reactphp.org

در فصل بعدی، به استقرار Reverb در محیط تولید و مقیاس‌پذیری افقی خواهیم پرداخت.