Lập trình · 19/09/2026

Laravel Reverb thực chiến: Xây realtime WebSocket và vận hành production

Laravel Reverb đưa WebSocket server vào hệ sinh thái Laravel, giúp xây thông báo, trạng thái đơn hàng, dashboard và cộng tác realtime mà không phải polling liên tục. Demo local khá nhanh, nhưng production cần thêm channel authorization, queue worker, TLS, reverse proxy, process manager, giới hạn kết nối và giám sát.

Laravel Reverb thực chiến: Xây realtime WebSocket và vận hành production

Laravel Reverb đưa WebSocket server vào hệ sinh thái Laravel, giúp xây thông báo, trạng thái đơn hàng, dashboard và cộng tác realtime mà không phải polling liên tục. Demo local khá nhanh, nhưng production cần thêm channel authorization, queue worker, TLS, reverse proxy, process manager, giới hạn kết nối và giám sát.

Luồng dữ liệu cần hiểu

  1. Trình duyệt dùng Laravel Echo mở WebSocket tới Reverb.
  2. Với private/presence channel, ứng dụng Laravel xác thực yêu cầu subscription.
  3. Backend dispatch event triển khai ShouldBroadcast.
  4. Queue worker xử lý broadcast job và gửi message tới Reverb.
  5. Reverb chuyển message tới các connection đã subscribe đúng channel.
Reverb giữ kết nối và phân phối message; quyền được nghe dữ liệu vẫn phải do ứng dụng quyết định.

1. Cài broadcasting với Reverb

php artisan install:broadcasting --reverb
npm install
npm run build

Lệnh cài tạo cấu hình broadcasting, Reverb, routes/channels.php, thông tin môi trường và scaffolding Echo. Không commit REVERB_APP_SECRET. Phân biệt hai nhóm biến: REVERB_SERVER_HOST/PORT là nơi process lắng nghe; REVERB_HOST/PORT/SCHEME là địa chỉ ứng dụng và browser sử dụng.

BROADCAST_CONNECTION=reverb
REVERB_SERVER_HOST=127.0.0.1
REVERB_SERVER_PORT=8080
REVERB_HOST=ws.example.com
REVERB_PORT=443
REVERB_SCHEME=https

2. Tạo một broadcast event

<?php

namespace App\Events;

use App\Models\Order;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;

class OrderStatusUpdated implements ShouldBroadcast
{
    use SerializesModels;

    public function __construct(public Order $order) {}

    public function broadcastOn(): array
    {
        return [new PrivateChannel('orders.'.$this->order->id)];
    }

    public function broadcastWith(): array
    {
        return [
            'id' => $this->order->id,
            'status' => $this->order->status,
            'updated_at' => $this->order->updated_at?->toISOString(),
        ];
    }
}

Chỉ gửi field frontend thực sự cần. Không serialize cả model có quan hệ, email nội bộ hoặc dữ liệu nhạy cảm. Broadcast mặc định được đưa vào queue; hãy chạy worker để event không nằm chờ vô thời hạn.

3. Authorization cho private channel

Trong routes/channels.php:

use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\Broadcast;

Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
    return Order::query()
        ->whereKey($orderId)
        ->where('user_id', $user->id)
        ->exists();
});

Public channel cho phép bất kỳ client nào subscribe nên chỉ dùng cho dữ liệu thực sự công khai. Private channel yêu cầu xác thực và callback phải kiểm tra quyền trên đúng resource. Presence channel còn trả thông tin thành viên; chỉ trả dữ liệu tối thiểu như ID và tên hiển thị.

php artisan channel:list

4. Lắng nghe bằng Laravel Echo

window.Echo.private(`orders.${orderId}`)
    .listen('OrderStatusUpdated', (event) => {
        updateOrderStatus(event.status);
    });

Nếu định nghĩa broadcastAs(), listener phải dùng tên tùy chỉnh với dấu chấm đầu, ví dụ .listen('.order.updated', ...). Khi component bị hủy, rời channel để tránh listener trùng và rò bộ nhớ.

5. Dispatch đúng thời điểm giao dịch

$order->update(['status' => 'shipped']);

OrderStatusUpdated::dispatch($order->fresh());

Nếu event được dispatch trong database transaction, worker có thể chạy trước khi commit và đọc trạng thái cũ. Dùng after-commit của queue hoặc triển khai ShouldDispatchAfterCommit cho event khi tính nhất quán quan trọng. Thiết kế payload có ID, version hoặc timestamp để client bỏ qua event đến trễ.

6. Chạy local và kiểm tra

php artisan reverb:start --debug
php artisan queue:work
npm run dev

--debug hữu ích ở local nhưng tạo log lớn, không nên bật thường xuyên trong production. Kiểm tra tab Network/WebSocket của trình duyệt, response của /broadcasting/auth, log queue và failed_jobs. Nếu WebSocket kết nối nhưng không có event, queue worker là điểm cần kiểm tra đầu tiên.

7. Đặt Reverb sau Nginx và TLS

Trong production, để Reverb nghe ở localhost và Nginx kết thúc TLS trên cổng 443:

server {
    listen 443 ssl http2;
    server_name ws.example.com;

    location / {
        proxy_http_version 1.1;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_read_timeout 60s;
        proxy_pass http://127.0.0.1:8080;
    }
}

Reverb dùng URI /app cho WebSocket và /apps cho API, nên proxy phải phục vụ cả hai. Chỉ mở 80/443 ra Internet; cổng 8080 nên bị giới hạn nội bộ. Cài chứng chỉ hợp lệ cho hostname WebSocket và dùng wss:// khi website chạy HTTPS.

8. Giới hạn origin và bảo vệ channel

Trong config/reverb.php, đặt allowed_origins thành các domain frontend cụ thể, không dùng * trên production nếu không thực sự cần. Origin check không thay thế authentication; attacker vẫn có thể gọi backend bằng công cụ riêng nếu authorization sai.

  • Dùng private channel cho dữ liệu người dùng hoặc tenant.
  • Kiểm tra tenant/resource trong callback, không chỉ kiểm tra đã đăng nhập.
  • Rate-limit endpoint tạo event và các client event.
  • Không đưa secret, token hoặc model đầy đủ vào payload.
  • Giới hạn kích thước message và số subscription hợp lý.

9. Chạy Reverb và queue bằng process manager

Reverb là process sống lâu. Dùng Supervisor hoặc systemd để tự khởi động lại. Ví dụ Supervisor:

[program:app-reverb]
command=php /var/www/app/artisan reverb:start
directory=/var/www/app
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/supervisor/app-reverb.log
stopasgroup=true
killasgroup=true

Queue worker là process riêng và cũng phải được giám sát. Sau deploy, chạy php artisan reverb:restart để kết thúc connection nhẹ nhàng rồi process manager khởi động phiên bản mới. Đồng thời restart queue worker để nạp code mới.

10. Capacity, monitoring và scale ngang

Mỗi WebSocket giữ memory và một file descriptor. Kiểm tra ulimit -n, giới hạn Nginx và Supervisor. Event loop mặc định dựa trên stream_select thường bị giới hạn khoảng 1.024 file; tài liệu Laravel khuyến nghị event loop ext-uv khi vượt khoảng 1.000 connection đồng thời.

Tích hợp Laravel Pulse để theo dõi connection và message; chỉ chạy daemon pulse:check trên một node khi scale ngang. Cảnh báo thêm memory, CPU, reconnect rate, queue lag, failed jobs và lỗi 4xx/5xx của endpoint authorization.

Khi một node không đủ, bật REVERB_SCALING_ENABLED=true, dùng Redis tập trung cho pub/sub và đặt nhiều Reverb node sau load balancer. Kiểm thử tải bằng mô hình connection thực tế, không chỉ số message/giây.

Những lỗi production thường gặp

  • WebSocket 404/502: Nginx chưa proxy đúng /app, process Reverb dừng hoặc sai port.
  • 403 subscription: session/cookie, CSRF, guard hoặc callback channel không khớp.
  • Event không đến: queue worker chưa chạy, broadcast job thất bại hoặc sai channel/event name.
  • Chỉ lỗi trên HTTPS: Echo vẫn dùng ws://, certificate sai hoặc proxy thiếu header Upgrade.
  • Deploy xong vẫn chạy code cũ: chưa restart Reverb và queue worker.
  • Rớt connection khi tải tăng: chạm giới hạn file descriptor, event loop, Nginx, memory hoặc port.

Checklist trước khi ra mắt

  1. Private/presence channel có test authorization cho phép và từ chối.
  2. Payload tối thiểu, không chứa secret hoặc dữ liệu tenant khác.
  3. Queue worker, Reverb và process manager đều hoạt động.
  4. WSS qua certificate hợp lệ; port nội bộ không public.
  5. allowed_origins chỉ chứa frontend hợp lệ.
  6. Deploy script restart Reverb và queue worker.
  7. Đã đo connection đồng thời, queue lag, memory và reconnect.
  8. Có dashboard, cảnh báo và kế hoạch scale Redis/load balancer.

Kết luận

Laravel Reverb làm phần realtime hòa vào event và broadcasting của Laravel, nhưng độ tin cậy đến từ toàn bộ đường truyền: authorization đúng, queue không nghẽn, reverse proxy hỗ trợ upgrade, process được giám sát và hạ tầng có giới hạn phù hợp. Bắt đầu bằng một use case nhỏ, đo tải thật rồi scale theo connection thay vì dự đoán.

Nguồn tham khảo

Thảo luận

Bình luận 0

Đăng nhập để bình luận

Bạn cần có tài khoản để tham gia thảo luận và trả lời độc giả khác.

Đăng nhậpĐăng ký

Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.