Showing posts with label Laravel. Show all posts
Showing posts with label Laravel. Show all posts

Wednesday, 9 April 2025

Use Redis Pub/Sub to check user online compare to not use

Khi sử dụng Redis Pub/Sub, hệ thống vẫn phải bắn sự kiện qua Private Channel của Reverb để gửi đến client. Tuy nhiên, Redis Pub/Sub mang lại hiệu suất cao hơn so với cách không dùng nó, và mình sẽ giải thích chi tiết tại sao, đồng thời làm rõ sự khác biệt giữa hai cách.


1. Cách hoạt động khi dùng Redis Pub/Sub

Quy trình

  1. User thay đổi trạng thái (ví dụ: User 500 offline):
    • Server gọi UserStatusService::setOffline(500).
    • Redis xóa trạng thái (user:500:status) và publish sự kiện đến các kênh riêng của bạn bè (ví dụ: user:1:status, user:2:status).
  2. Worker Redis Subscriber:
    • Lắng nghe tất cả kênh user:*:status.
    • Khi nhận message từ Redis (ví dụ: user:1:status), worker broadcast sự kiện qua Reverb đến kênh private-user.1.
  3. Client:
    • Mỗi user lắng nghe kênh riêng của mình (ví dụ: private-user.1) qua Laravel Echo.
    • Nhận sự kiện và cập nhật UI.

Tại sao vẫn cần Private Channel?

  • Redis Pub/Sub không giao tiếp trực tiếp với client: Redis chỉ là hệ thống lưu trữ và truyền tin nội bộ giữa các server/process. Nó không thể gửi dữ liệu trực tiếp đến browser qua WebSocket.
  • Reverb (WebSocket): Đảm nhận việc gửi sự kiện từ server đến client qua kết nối WebSocket. Private Channel giúp gửi đúng đối tượng (chỉ bạn bè nhận được thông báo).
  • Kết hợp: Redis Pub/Sub truyền tin nhanh trong backend, Reverb chuyển tiếp tin đó đến client qua Private Channel.

2. So sánh với cách không dùng Redis Pub/Sub

Không dùng Redis Pub/Sub

  1. Quy trình:
    • User 500 offline → Server truy vấn database để lấy danh sách bạn bè (User 1, User 2, User 3).
    • Server broadcast sự kiện qua Reverb đến các kênh private-user.1, private-user.2, private-user.3 bằng cách gọi event() trực tiếp trong PHP:
      php
      foreach ($friendIds as $friendId) { event(new UserStatusChanged($userId, 'offline', $friendId)); }
  2. Nhược điểm:
    • Truy vấn database: Mỗi lần user thay đổi trạng thái, phải query bảng friends (hoặc tương tự) để lấy danh sách bạn bè → chậm, đặc biệt nếu danh sách lớn (1000 bạn).
    • Tải xử lý: PHP phải thực hiện vòng lặp và gửi từng event trong cùng một request → tốn CPU và thời gian.

Dùng Redis Pub/Sub

  1. Quy trình:
    • User 500 offline → Server lấy danh sách bạn bè từ Redis (nếu đã cache) hoặc database, rồi publish đến các kênh Redis (user:1:status, user:2:status).
    • Worker Redis subscribe nhận message và broadcast qua Reverb.
  2. Ưu điểm:
    • Lấy dữ liệu nhanh hơn: Nếu danh sách bạn bè được cache trong Redis (ví dụ: user:500:friends), truy vấn từ Redis (in-memory) nhanh hơn rất nhiều so với database (disk-based).
    • Xử lý bất đồng bộ: Redis Pub/Sub và worker chạy độc lập, không chặn request chính của PHP → giảm tải server.
    • Tốc độ publish: Redis xử lý hàng nghìn message/giây, nhẹ hơn việc PHP gọi event() trong vòng lặp.

3. Tại sao Redis Pub/Sub nhanh hơn?

Lý do chính

  1. Truy vấn nhanh từ Redis:
    • Không dùng Redis Pub/Sub: Mỗi lần broadcast cần query database để lấy danh sách bạn bè → tốn thời gian (vài ms đến hàng chục ms tùy kích thước bảng).
    • Dùng Redis Pub/Sub: Nếu danh sách bạn bè đã cache trong Redis (ví dụ: user:500:friends dưới dạng Set), truy xuất chỉ mất <1ms vì Redis là in-memory.
  2. Bất đồng bộ:
    • Không dùng Redis: PHP xử lý toàn bộ logic (query + broadcast) trong một request → đồng bộ, dễ gây bottleneck.
    • Dùng Redis Pub/Sub: Publish là hành động tức thời, worker xử lý song song → không chặn request chính.
  3. Hiệu suất Pub/Sub:
    • Redis Pub/Sub được thiết kế để truyền tin nhanh, không cần lưu trữ lâu dài, phù hợp cho sự kiện realtime.
    • Gửi 1000 message qua Redis Pub/Sub nhanh hơn gọi 1000 lần event() trong PHP.

Minh họa thời gian

  • Không Redis: Query database (10ms) + vòng lặp 1000 bạn (50ms) = ~60ms cho 1 sự kiện.
  • Có Redis: Cache Redis (1ms) + publish 1000 kênh (5ms) = ~6ms, cộng với worker xử lý bất đồng bộ.

4. Ví dụ cụ thể

Không dùng Redis Pub/Sub

php
public static function setOffline($userId) { Redis::del("user:$userId:status"); $friendIds = User::find($userId)->friends()->pluck('id')->toArray(); // Query database foreach ($friendIds as $friendId) { event(new \App\Events\UserStatusChanged($userId, 'offline', $friendId)); // Gửi từng event } }
  • Chậm vì: Query database + vòng lặp tuần tự.

Dùng Redis Pub/Sub

php
public static function setOffline($userId) { Redis::del("user:$userId:status"); $friendIds = Redis::smembers("user:$userId:friends"); // Lấy từ Redis if (!$friendIds) { $friendIds = User::find($userId)->friends()->pluck('id')->toArray(); Redis::sadd("user:$userId:friends", $friendIds); // Cache lần đầu } foreach ($friendIds as $friendId) { Redis::publish("user:$friendId:status", json_encode(['userId' => $userId, 'status' => 'offline'])); } }
  • Nhanh vì: Lấy từ Redis (nếu đã cache) + publish bất đồng bộ.

Worker Redis

php
public function handle() { Redis::psubscribe(['user:*:status'], function ($message, $channel) { $data = json_decode($message, true); $friendId = explode(':', $channel)[1]; broadcast(new \App\Events\UserStatusChanged($data['userId'], $data['status'], $friendId)); }); }

5. Kết luận

  • Vẫn cần Private Channel: Redis Pub/Sub chỉ thay thế bước gửi sự kiện nội bộ trong backend. Reverb dùng Private Channel để gửi từ server đến client.
  • Nhanh hơn vì:
    1. Truy xuất danh sách bạn bè từ Redis thay vì database.
    2. Publish qua Redis bất đồng bộ, nhẹ hơn gọi event() tuần tự trong PHP.
  • Hiệu quả: Với 1000 user, cách này giảm tải server và tăng tốc độ xử lý từ vài chục ms xuống vài ms.

Thank you

Monday, 31 March 2025

10 Câu Hỏi Nâng Cao Về MySQL & PHP

 10 Câu Hỏi Nâng Cao Về MySQL

  1. Trong một hệ thống có hàng triệu bản ghi, làm thế nào để bạn tối ưu hóa hiệu suất của các truy vấn phức tạp liên quan đến nhiều bảng, đặc biệt khi sử dụng các phép JOIN và GROUP BY?

(Kiểm tra khả năng tối ưu hóa truy vấn ở quy mô lớn.)

  1. Bạn đã từng làm việc với MySQL partitioning chưa? Hãy mô tả một tình huống cụ thể mà bạn đã sử dụng partitioning để cải thiện hiệu suất, và kết quả ra sao?

(Tập trung vào kỹ thuật nâng cao để quản lý dữ liệu lớn.)

  1. Khi thiết kế một hệ thống cần đảm bảo tính sẵn sàng cao (high availability) với MySQL, bạn sẽ triển khai kiến trúc nào, và làm thế nào để xử lý các vấn đề như failover và đồng bộ dữ liệu?

(Kiểm tra kinh nghiệm với kiến trúc hệ thống và HA.)

  1. Làm thế nào để bạn xử lý các vấn đề về deadlock trong MySQL khi nhiều giao dịch đồng thời truy cập cùng một tập dữ liệu? Hãy đưa ra một ví dụ cụ thể.

(Tập trung vào quản lý giao dịch và giải quyết xung đột.)

  1. MySQL có hỗ trợ full-text search. Bạn đã từng sử dụng tính năng này chưa? Nếu có, hãy mô tả cách bạn triển khai và những hạn chế bạn gặp phải.

(Kiểm tra kinh nghiệm với tính năng nâng cao của MySQL.)

  1. Khi làm việc với MySQL trong một ứng dụng Laravel, làm thế nào để bạn xử lý các truy vấn phức tạp mà Eloquent không thể đáp ứng một cách hiệu quả?

(Kết hợp kinh nghiệm Laravel với MySQL nâng cao.)

  1. Bạn đã từng sử dụng EXPLAIN ANALYZE trong MySQL chưa? Hãy giải thích cách bạn sử dụng nó để tối ưu hóa một truy vấn cụ thể trong một dự án.

(Tập trung vào phân tích và tối ưu hóa truy vấn.)

  1. Làm thế nào để bạn triển khai một hệ thống sao lưu (backup) và khôi phục (restore) cho MySQL trong môi trường production, và bạn xử lý các vấn đề về dữ liệu lớn như thế nào?

(Kiểm tra kinh nghiệm DevOps và quản lý cơ sở dữ liệu.)

  1. MySQL có hỗ trợ các tính năng như CTE (Common Table Expressions) và window functions. Bạn đã từng sử dụng chúng chưa? Hãy đưa ra một ví dụ cụ thể.

(Tập trung vào các tính năng SQL nâng cao.)

  1. Khi làm việc với một hệ thống phân tán, làm thế nào để bạn đảm bảo tính nhất quán dữ liệu (data consistency) giữa các node MySQL, và bạn đã gặp phải thách thức gì?

(Kiểm tra kinh nghiệm với hệ thống phân tán và đồng bộ dữ liệu.)


10 Câu Hỏi Nâng Cao Về PHP

  1. Trong một ứng dụng Laravel có lưu lượng truy cập cao, làm thế nào để bạn triển khai một hệ thống hàng đợi (queue) để xử lý các tác vụ nặng như gửi email hoặc xử lý dữ liệu lớn? Hãy mô tả chi tiết.

(Tập trung vào xử lý tác vụ bất đồng bộ và tối ưu hóa hiệu suất.)

  1. Bạn đã từng sử dụng PHP để xây dựng một hệ thống microservices chưa? Nếu có, hãy mô tả cách bạn thiết kế và những thách thức bạn gặp phải.

(Kiểm tra kinh nghiệm với kiến trúc microservices.)

  1. Làm thế nào để bạn triển khai một hệ thống caching phân tán (distributed caching) trong Laravel, và bạn đã sử dụng nó để giải quyết vấn đề gì trong một dự án?

(Tập trung vào tối ưu hóa hiệu suất với caching.)

  1. Khi làm việc với Laravel, làm thế nào để bạn xử lý các vấn đề về hiệu suất khi ứng dụng phải xử lý hàng nghìn request mỗi giây? Hãy đưa ra ví dụ cụ thể.

(Kiểm tra kinh nghiệm tối ưu hóa hiệu suất ở quy mô lớn.)

  1. Bạn đã từng sử dụng PHP để triển khai một hệ thống event-driven chưa? Hãy mô tả cách bạn thiết kế và những công cụ bạn sử dụng.

(Tập trung vào kiến trúc event-driven và xử lý sự kiện.)

  1. Làm thế nào để bạn triển khai một hệ thống xác thực (authentication) tùy chỉnh trong Laravel cho một ứng dụng doanh nghiệp, và bạn xử lý các yêu cầu bảo mật phức tạp như thế nào?

(Kiểm tra kinh nghiệm với bảo mật và tùy chỉnh Laravel.)

  1. Bạn đã từng làm việc với PHP để xử lý các tác vụ đồng thời (concurrency) chưa? Hãy mô tả cách bạn sử dụng các công cụ như Swoole hoặc các kỹ thuật khác.

(Tập trung vào xử lý đồng thời trong PHP.)

  1. Khi làm việc với một ứng dụng Laravel lớn, làm thế nào để bạn quản lý các dependency và đảm bảo mã có thể kiểm thử (testable)? Hãy đưa ra ví dụ cụ thể.

(Kiểm tra kiến thức về thiết kế phần mềm và kiểm thử.)

  1. Bạn đã từng sử dụng PHP để xây dựng một hệ thống xử lý dữ liệu thời gian thực (real-time) chưa? Nếu có, hãy mô tả cách bạn triển khai và những thách thức bạn gặp phải.

(Tập trung vào ứng dụng thời gian thực và WebSocket.)

  1. Làm thế nào để bạn triển khai một hệ thống logging và monitoring trong một ứng dụng Laravel để phát hiện và xử lý lỗi trong môi trường production?

(Kiểm tra kinh nghiệm DevOps và quản lý ứng dụng.)


Tuesday, 25 February 2025

Building a Scalable & Secure Private Chat with Laravel 11, Reverb, Redis & Vue 3 + TypeScript

This guide walks through creating a production-ready real-time private chat system using:
Laravel 11 (Backend)
Reverb + Redis (Real-time WebSockets)
Vue 3 + TypeScript (Frontend)
Nginx Load Balancer (Multi-server Scaling)


🔹 Backend: Laravel 11 + Reverb + Redis

1️⃣ Setup Laravel 11 & Dependencies

bash
composer create-project laravel/laravel chat-app cd chat-app composer require predis/predis

2️⃣ Install & Configure Laravel Reverb

bash
php artisan reverb:install php artisan vendor:publish --tag=reverb-config

🔹 Modify .env

env
BROADCAST_CONNECTION=redis QUEUE_CONNECTION=redis

🔹 Modify config/broadcasting.php

php
'connections' => [ 'redis' => [ 'driver' => 'redis', 'connection' => 'default', ], ],

🔹 Start Redis

bash
redis-server

🛠 Step 2: Database & Models

1️⃣ Create Chat Model & Migration

bash
php artisan make:model Chat -m

Define chats table structure

php
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up() { Schema::create('chats', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('sender_id'); $table->unsignedBigInteger('receiver_id'); $table->text('message'); // Store encrypted messages $table->timestamps(); $table->foreign('sender_id')->references('id')->on('users')->onDelete('cascade'); $table->foreign('receiver_id')->references('id')->on('users')->onDelete('cascade'); }); } public function down() { Schema::dropIfExists('chats'); } };

Run Migration

bash
php artisan migrate

🛠 Step 3: Secure Private Channels

1️⃣ Define Private Channel Authorization

Edit routes/channels.php:

php
use Illuminate\Support\Facades\Broadcast; Broadcast::channel('chat.{receiverId}', function ($user, $receiverId) { return (int) $user->id === (int) $receiverId; });

🛠 Step 4: Broadcast Events Securely

1️⃣ Create Chat Event

bash
php artisan make:event MessageSent

Modify MessageSent.php

php
namespace App\Events; use App\Models\Chat; use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; use Illuminate\Queue\SerializesModels; class MessageSent implements ShouldBroadcastNow { use InteractsWithSockets, SerializesModels; public $chat; public function __construct(Chat $chat) { $this->chat = $chat; } public function broadcastOn() { return new PrivateChannel('chat.' . $this->chat->receiver_id); } public function broadcastWith() { return [ 'message' => $this->chat->message, 'sender_id' => $this->chat->sender_id, 'receiver_id' => $this->chat->receiver_id, 'timestamp' => $this->chat->created_at->toDateTimeString(), ]; } }

🛠 Step 5: Send Messages

1️⃣ Create ChatController.php

bash
php artisan make:controller ChatController

2️⃣ Store & Broadcast Messages Securely

Modify ChatController.php:

php
namespace App\Http\Controllers; use App\Events\MessageSent; use App\Models\Chat; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; class ChatController extends Controller { public function sendMessage(Request $request) { $request->validate([ 'receiver_id' => 'required|exists:users,id', 'message' => 'required|string', ]); // Encrypt message before storing $chat = Chat::create([ 'sender_id' => Auth::id(), 'receiver_id' => $request->receiver_id, 'message' => encrypt($request->message), ]); // Broadcast event broadcast(new MessageSent($chat))->toOthers(); return response()->json(['message' => 'Sent successfully']); } }

🔹 Frontend: Vue 3 + TypeScript

🛠 Step 1: Setup Vue 3 + TypeScript

bash
npm create vue@latest chat-app-frontend cd chat-app-frontend npm install npm install @vueuse/core axios pusher-js laravel-echo

🛠 Step 2: Configure Laravel Echo

Modify src/plugins/echo.ts:

ts
import Echo from 'laravel-echo'; import Pusher from 'pusher-js'; window.Pusher = Pusher; const echo = new Echo({ broadcaster: 'pusher', key: 'reverb', wsHost: import.meta.env.VITE_APP_WS_HOST, wsPort: 6001, wssPort: 6001, forceTLS: false, disableStats: true, enabledTransports: ['ws', 'wss'] }); export default echo;

🔹 Modify .env

ini
VITE_APP_WS_HOST=localhost

🛠 Step 3: Chat Component

Create src/components/Chat.vue:

vue
<script setup lang="ts"> import { ref, onMounted } from 'vue'; import axios from 'axios'; import echo from '@/plugins/echo'; const userId = 1; // Replace with auth user ID const messages = ref<{ sender_id: number; message: string }[]>([]); const newMessage = ref(''); const sendMessage = async () => { await axios.post('http://localhost:8000/api/send-message', { receiver_id: 2, // Change to actual receiver message: newMessage.value }); newMessage.value = ''; }; onMounted(() => { echo.private(`chat.${userId}`) .listen('MessageSent', (e: any) => { messages.value.push(e); }); }); </script> <template> <div> <div v-for="msg in messages" :key="msg.message"> <strong v-if="msg.sender_id === userId">You:</strong> <strong v-else>Friend:</strong> {{ msg.message }} </div> <input v-model="newMessage" @keyup.enter="sendMessage" placeholder="Type a message..." /> <button @click="sendMessage">Send</button> </div> </template>

🔹 Deployment: Multi-Server Scaling

🛠 Step 1: Configure Load Balancer

Modify Nginx config:

nginx
upstream websocket_servers { server 192.168.1.101:6001; server 192.168.1.102:6001; } server { listen 80; server_name example.com; location /reverb { proxy_pass http://websocket_servers; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Host $host; } }

🚀 Summary

Private Chat with Laravel 11 + Reverb + Redis
Vue 3 + TypeScript Frontend
Private Channels for Authentication
Load Balanced WebSockets for Multi-Server Scaling

💡 Next Step: Want a Docker setup for full production deployment? 🚀

Building a private chat real-time system using Laravel 11, Reverb, and Redis.

Here’s a step-by-step guide to building a private chat real-time system using Laravel 11, Reverb, and Redis.


Step 1: Install Laravel 11

If you haven’t already installed Laravel 11, create a new project:

bash
composer create-project laravel/laravel chat-app cd chat-app

Then, install dependencies:

bash
composer require predis/predis

Step 2: Install & Configure Laravel Reverb

1️⃣ Install Reverb

bash
php artisan reverb:install

2️⃣ Publish Reverb config file

bash
php artisan vendor:publish --tag=reverb-config

3️⃣ Update .env to use Redis broadcasting

env
BROADCAST_CONNECTION=redis QUEUE_CONNECTION=redis

4️⃣ Update config/broadcasting.php to use Redis

php
'connections' => [ 'redis' => [ 'driver' => 'redis', 'connection' => 'default', ], ],

5️⃣ Start Redis

bash
redis-server

Step 3: Create WebSocket Channel for Private Chat

1️⃣ Define a private channel in routes/channels.php

php
use Illuminate\Support\Facades\Broadcast; Broadcast::channel('chat.{receiverId}', function ($user, $receiverId) { return (int) $user->id === (int) $receiverId; });

💡 This ensures only authenticated users can listen to their own chat events.


Step 4: Create a Chat Model & Migration

1️⃣ Generate Chat Model & Migration

bash
php artisan make:model Chat -m

2️⃣ Define the Schema in database/migrations/YYYY_MM_DD_create_chats_table.php

php
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up() { Schema::create('chats', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('sender_id'); $table->unsignedBigInteger('receiver_id'); $table->text('message'); $table->timestamps(); $table->foreign('sender_id')->references('id')->on('users')->onDelete('cascade'); $table->foreign('receiver_id')->references('id')->on('users')->onDelete('cascade'); }); } public function down() { Schema::dropIfExists('chats'); } };

3️⃣ Run Migration

bash
php artisan migrate

Step 5: Create Chat Event

1️⃣ Generate an Event

bash
php artisan make:event MessageSent

2️⃣ Edit app/Events/MessageSent.php

php
namespace App\Events; use App\Models\Chat; use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; use Illuminate\Queue\SerializesModels; class MessageSent implements ShouldBroadcastNow { use InteractsWithSockets, SerializesModels; public $chat; public function __construct(Chat $chat) { $this->chat = $chat; } public function broadcastOn() { return new PrivateChannel('chat.' . $this->chat->receiver_id); } public function broadcastWith() { return [ 'message' => $this->chat->message, 'sender_id' => $this->chat->sender_id, 'receiver_id' => $this->chat->receiver_id, 'timestamp' => $this->chat->created_at->toDateTimeString(), ]; } }

💡 This event will broadcast new messages to the receiver’s private channel.


Step 6: Create Chat Controller

1️⃣ Generate a Chat Controller

bash
php artisan make:controller ChatController

2️⃣ Edit app/Http/Controllers/ChatController.php

php
namespace App\Http\Controllers; use App\Events\MessageSent; use App\Models\Chat; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; class ChatController extends Controller { public function sendMessage(Request $request) { $request->validate([ 'receiver_id' => 'required|exists:users,id', 'message' => 'required|string', ]); $chat = Chat::create([ 'sender_id' => Auth::id(), 'receiver_id' => $request->receiver_id, 'message' => $request->message, ]); broadcast(new MessageSent($chat))->toOthers(); return response()->json(['message' => 'Sent successfully']); } }

💡 This method stores the message and broadcasts it to the receiver.


Step 7: Define API Route

Edit routes/api.php:

php
use App\Http\Controllers\ChatController; Route::middleware('auth:sanctum')->post('/send-message', [ChatController::class, 'sendMessage']);

💡 Ensure your API uses auth:sanctum or another authentication method.


Step 8: Start Reverb WebSocket Server

1️⃣ Run Laravel Queues (Redis required)

bash
php artisan queue:work

2️⃣ Start Laravel Reverb

bash
php artisan reverb:start

Step 9: Frontend Integration (Example in JavaScript)

Here’s how to listen for messages using Laravel Echo:

1️⃣ Install Laravel Echo & Pusher JS

bash
npm install laravel-echo pusher-js

2️⃣ Configure Laravel Echo in resources/js/bootstrap.js

js
import Echo from 'laravel-echo'; import Pusher from 'pusher-js'; window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'pusher', key: 'reverb', wsHost: window.location.hostname, wsPort: 6001, wssPort: 6001, forceTLS: false, disableStats: true, enabledTransports: ['ws', 'wss'] });

3️⃣ Listen for messages (Example in Vue.js or React)

js
window.Echo.private(`chat.${userId}`) .listen('MessageSent', (e) => { console.log("New message received:", e.message); });

Step 10: Test Everything

1️⃣ Start Laravel Backend

bash
php artisan serve

2️⃣ Start Reverb

bash
php artisan reverb:start

3️⃣ Send a Message using Postman or Frontend

json
POST /api/send-message { "receiver_id": 2, "message": "Hello, how are you?" }

4️⃣ Check if the receiver gets the real-time message!


How to Scale Laravel Reverb for Multi-Server?

  1. Ensure Redis is used for broadcasting (BROADCAST_CONNECTION=redis).
  2. Deploy Laravel on multiple servers.
  3. Run php artisan reverb:start on each server.
  4. Use a Load Balancer (e.g., Nginx, AWS ALB) to route WebSocket traffic.
  5. Enable Redis clustering for large-scale chat applications.

Summary

Laravel Reverb provides a native WebSocket solution for Laravel 11.
Redis is used to broadcast events across multiple servers.
Private Channels ensure secure messaging.
Frontend listens for messages using Laravel Echo.
For multi-server support, use Redis + Load Balancer.

Would you like a Docker setup for a production-ready chat app? 🚀


Xác thực Private Chat trong Laravel Reverb

Để đảm bảo chỉ người dùng hợp lệ mới có thể lắng nghe và nhận tin nhắn trong Private Chat, Laravel sử dụng Private Channels kết hợp với cơ chế xác thực.


1. Xác thực Private Channel

Trong routes/channels.php:

php
use Illuminate\Support\Facades\Broadcast; Broadcast::channel('chat.{receiverId}', function ($user, $receiverId) { return (int) $user->id === (int) $receiverId; });

🔹 Giải thích:

  • Khi một người dùng cố gắng lắng nghe kênh chat.{receiverId}, Laravel sẽ gọi callback xác thực.
  • Chỉ khi $user->id === $receiverId, Laravel mới cho phép người dùng lắng nghe kênh.
  • Điều này đảm bảo chỉ người nhận tin nhắn mới có thể nghe tin nhắn đến.

2. Xác thực Người Dùng Trước Khi Gửi Tin Nhắn

Trong ChatController.php, Laravel bắt buộc user phải đăng nhập để gửi tin nhắn:

php
public function sendMessage(Request $request) { $request->validate([ 'receiver_id' => 'required|exists:users,id', 'message' => 'required|string', ]); $chat = Chat::create([ 'sender_id' => Auth::id(), 'receiver_id' => $request->receiver_id, 'message' => $request->message, ]); broadcast(new MessageSent($chat))->toOthers(); return response()->json(['message' => 'Sent successfully']); }

🔹 Giải thích:

  • Auth::id() lấy ID của user hiện tại (bắt buộc phải login).
  • Chặn gửi tin nhắn đến user không tồn tại với exists:users,id.
  • Dữ liệu chỉ được lưu & phát đi nếu user hợp lệ.

3. Xác thực Khi Lắng Nghe Kênh Trên Frontend

Laravel Echo sẽ gửi request xác thực trước khi lắng nghe Private Channel.

Ví dụ, trong Vue.js/React/JavaScript:

js
window.Echo.private(`chat.${userId}`) .listen('MessageSent', (e) => { console.log("Tin nhắn mới:", e.message); });

📌 Lưu ý:

  • Laravel sẽ tự động chặn kết nối nếu user không được phép nghe kênh chat.{receiverId}.
  • Nếu user chưa đăng nhập, Laravel sẽ từ chối kết nối.

4. Laravel Xác Thực Private Channel Như Thế Nào?

Khi frontend yêu cầu lắng nghe một Private Channel, Laravel sẽ:

  1. Kiểm tra user đã đăng nhập hay chưa.
  2. Chạy callback trong routes/channels.php để xem user có quyền nghe không.
  3. Trả về lỗi nếu không hợp lệ, hoặc cho phép kết nối nếu hợp lệ.

📌 Tổng Kết

Private Channel (chat.{receiverId}) đảm bảo chỉ người nhận mới được nghe tin nhắn.
Laravel Echo gửi yêu cầu xác thực trước khi cho phép kết nối.
Backend yêu cầu user phải đăng nhập khi gửi tin nhắn (Auth::id()).
Nếu không xác thực, user không thể nhận tin nhắn private.

🔹 Với cách này, tin nhắn của user sẽ an toàn và không bị nghe lén. 🚀

Publish npm package

  Để publish   pav-kit  lên NPM, bạn hãy làm theo các bước dưới đây. Tôi đã tạo thêm file  index.js  để đảm bảo gói tin hợp lệ. Bước 1: Tạo ...