Ứng Dụng Real-Time Với Laravel Reverb

· 5 min read

Trong nhiều năm, các lập trình viên Laravel dựa vào Pusher (đắt đỏ) hoặc Laravel Echo Server / Soketi (cần Node.js) cho WebSockets.

Laravel 11 giới thiệu Reverb, một WebSocket server được viết trực tiếp bằng PHP (sử dụng Event Loop). Nó cho phép bạn xây dựng các ứng dụng real-time mà không cần rời khỏi hệ sinh thái PHP.

Tại Sao Chọn Reverb?

  • Native PHP: Không cần Node.js
  • Miễn phí hoàn toàn: Self-hosted, không tính phí theo message
  • First-party: Được maintain bởi Laravel team
  • Nhanh: Xây dựng trên ReactPHP, xử lý hàng nghìn kết nối
  • Đơn giản: Hoạt động với Laravel Broadcasting sẵn có

Cài Đặt

php artisan install:broadcasting

Lệnh này hỏi bạn có muốn cài đặt Reverb không. Chọn "Yes".

Nó cài đặt package, publish cấu hình, và cấu hình môi trường của bạn.

Cài Đặt Thủ Công

composer require laravel/reverb
php artisan reverb:install

Cấu hình .env:

BROADCAST_DRIVER=reverb
REVERB_APP_ID=my-app-id
REVERB_APP_KEY=my-app-key
REVERB_APP_SECRET=my-app-secret
REVERB_HOST=localhost
REVERB_PORT=8080

Cách Hoạt Động

  1. Backend Event: Bạn fire một event trong PHP.
    MessageSent::dispatch($message);
    
  2. Broadcasting: Nếu event implement ShouldBroadcast, Laravel đẩy nó tới Reverb server (qua Redis hoặc kết nối trực tiếp).
  3. Client (Laravel Echo): Frontend đang lắng nghe một channel và nhận JSON payload ngay lập tức.

Event

namespace App\Events;

use App\Models\Message;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class MessageSent implements ShouldBroadcast
{
    public function __construct(
        public Message $message
    ) {}

    public function broadcastOn(): array
    {
        return [
            new PrivateChannel('chat.' . $this->message->room_id),
        ];
    }

    // Tùy chỉnh payload
    public function broadcastWith(): array
    {
        return [
            'id' => $this->message->id,
            'content' => $this->message->content,
            'user' => $this->message->user->only(['id', 'name', 'avatar']),
            'created_at' => $this->message->created_at->toISOString(),
        ];
    }
}

Channel Authorization

Với private channels, định nghĩa authorization trong routes/channels.php:

use App\Models\Room;

Broadcast::channel('chat.{roomId}', function ($user, $roomId) {
    return Room::find($roomId)?->users->contains($user);
});

Frontend (Laravel Echo)

Cài đặt các packages cần thiết:

npm install laravel-echo pusher-js

Cấu hình Echo trong resources/js/bootstrap.js:

import Echo from 'laravel-echo';
import Pusher from 'pusher-js';

window.Pusher = Pusher;

window.Echo = new Echo({
    broadcaster: 'reverb',
    key: import.meta.env.VITE_REVERB_APP_KEY,
    wsHost: import.meta.env.VITE_REVERB_HOST,
    wsPort: import.meta.env.VITE_REVERB_PORT,
    wssPort: import.meta.env.VITE_REVERB_PORT,
    forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    enabledTransports: ['ws', 'wss'],
});

Lắng nghe events trong component:

Echo.private(`chat.${roomId}`)
    .listen('MessageSent', (e) => {
        console.log('Tin nhắn mới:', e);
        messages.value.push(e);
    })
    .listenForWhisper('typing', (e) => {
        console.log(`${e.name} đang gõ...`);
    });

Presence Channels

Theo dõi ai đang online với presence channels:

// routes/channels.php
Broadcast::channel('room.{roomId}', function ($user, $roomId) {
    if (Room::find($roomId)?->users->contains($user)) {
        return ['id' => $user->id, 'name' => $user->name];
    }
});
Echo.join(`room.${roomId}`)
    .here((users) => {
        onlineUsers.value = users;
    })
    .joining((user) => {
        onlineUsers.value.push(user);
    })
    .leaving((user) => {
        onlineUsers.value = onlineUsers.value.filter(u => u.id !== user.id);
    });

Client-Side Events (Whispers)

Gửi events trực tiếp từ client tới client (ví dụ: chỉ báo "đang gõ"):

// Gửi
Echo.private(`chat.${roomId}`).whisper('typing', {
    name: currentUser.name
});

// Nhận
.listenForWhisper('typing', (e) => {
    typingUser.value = e.name;
});

Chạy Reverb trong Production

Khởi động Reverb server:

php artisan reverb:start

Cho production, sử dụng Supervisor để giữ nó chạy:

[program:reverb]
command=php /var/www/html/artisan reverb:start
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/reverb.log

Khả Năng Mở Rộng với Redis

Để mở rộng theo chiều ngang, Reverb sử dụng Redis Pub/Sub để đồng bộ hóa nhiều servers:

REVERB_SCALING_ENABLED=true
REDIS_HOST=your-redis-host

Điều này cho phép bạn chạy nhiều instances Reverb phía sau load balancer.

Các Trường Hợp Sử Dụng

  • Ứng dụng chat
  • Trung tâm thông báo
  • Metrics dashboard real-time
  • Chỉ báo "Người dùng đang gõ..."
  • Cộng tác real-time (kiểu Google Docs)
  • Điểm số thể thao trực tiếp
  • Triển khai multiplayer game

Debugging

Bật debug mode để xem connection logs:

php artisan reverb:start --debug

Real-time không còn "khó" trong PHP nữa. Với Reverb, nó là native, hiệu năng cao, và tiết kiệm chi phí.

Bình luận