laravelwebsocketredisratchetbroadcasting

Does Ratchet WebSocket require Redis to broadcast messages from Laravel API, but not when the events sending directly from frontend?


I'm implementing a real-time stock opname (inventory check) feature using Laravel and Ratchet WebSocket.

I want to broadcast a message to all connected WebSocket clients when an API endpoint is hit, for example:

// Laravel controller
public function startStockOpname(Request $request) {
    Redis::publish("stock-opname.{$branchCode}", json_encode([
        'message' => 'Stock Opname Started',
        'role' => 'auditor',
        'branch_code' => $branchCode,
    ]));
} 

On the other side, the WebSocket server (Ratchet) is subscribing to the Redis channels and then broadcasting messages to clients.

This is the flow I aim for:

Frontend ➜ API call to Laravel ➜ Laravel publishes via Redis ➜ WebSocket server receives ➜ Broadcast to WebSocket clients

But it doesn't work.

However, if I send the message directly from the frontend via websocket.send(), everything works perfectly and all clients receive the message.

this.websocket.onmessage = (event) => {
                const data = JSON.parse(event.data);
                console.log('Message received:', data);

                // Jika auditor memulai stock opname
                if (data.message === 'Stock Opname Started/Resumed' && data.role === 'auditor') {
                    this.isDisabledStart = false;
                    this.remainingSeconds = 60;
                    this.startCountdownTimer();
                }
            };

So now I'm confused:

Questions:

  1. When using Ratchet in Laravel, is Redis mandatory if I want to send messages from Laravel (API/controller) to WebSocket clients?

  2. Is Redis not needed if the frontend directly sends messages to the WebSocket server via .send()?

  3. Why would Redis::publish() in Laravel fail to trigger a broadcast in Ratchet, even though everything seems connected?


Solution

  • Clarification on Your Questions

    1. Is Redis mandatory to communicate from Laravel to WebSocket clients?

    Yes, Redis or any other message broker is essential if Laravel and the WebSocket server are running in separate processes. Laravel cannot directly call methods in the Ratchet server; it must use a broker like Redis to pass messages between these isolated components.

    2. Is Redis not needed if the frontend sends messages via .send()?

    Correct. In this case, the frontend communicates directly with the WebSocket server. Redis is unnecessary because there is no Laravel interaction involved in message delivery.

    3. Why does Redis::publish() in Laravel not trigger any response in Ratchet?

    The WebSocket server likely uses a blocking subscription (psubscribe) that:

    Additionally, Laravel and Ratchet may be connected to different Redis instances, ports, or databases, which should be verified.

    Solution

    Use an Asynchronous Redis Client Compatible with the Ratchet Event Loop

    The correct way to receive Redis messages in a non-blocking way is by using the react/redis package, which is fully compatible with the Ratchet event loop (ReactPHP). This allows the WebSocket server to simultaneously listen to Redis and handle WebSocket client connections.

    Step-by-Step Implementation

    1. Install Required Packages

    Install react/redis and Ratchet (if not already installed):

    composer require clue/redis-react
    composer require cboden/ratchet
    

    2. Update Your WebSocket Server

    Here is a simplified example of a WebSocket server using Ratchet and react/redis to handle Redis messages and broadcast them to WebSocket clients:

    use Ratchet\MessageComponentInterface;
    use Ratchet\ConnectionInterface;
    use React\EventLoop\Factory as LoopFactory;
    use React\Socket\Server as SocketServer;
    use Ratchet\Server\IoServer;
    use Ratchet\Http\HttpServer;
    use Ratchet\WebSocket\WsServer;
    use Clue\React\Redis\Factory as RedisFactory;
    
    class StockOpnameServer implements MessageComponentInterface
    {
        public $clients;
    
        public function __construct()
        {
            $this->clients = new \SplObjectStorage;
        }
    
        public function onOpen(ConnectionInterface $conn)
        {
            $this->clients->attach($conn);
        }
    
        public function onMessage(ConnectionInterface $from, $msg) {}
    
        public function onClose(ConnectionInterface $conn)
        {
            $this->clients->detach($conn);
        }
    
        public function onError(ConnectionInterface $conn, \Exception $e)
        {
            $conn->close();
        }
    
        public function broadcast($data)
        {
            foreach ($this->clients as $client) {
                $client->send($data);
            }
        }
    }
    
    // Create ReactPHP loop
    $loop = LoopFactory::create();
    
    // Create the WebSocket server instance
    $stockServer = new StockOpnameServer();
    
    // Set up WebSocket server
    $socket = new SocketServer('0.0.0.0:8080', $loop);
    $server = new IoServer(
        new HttpServer(
            new WsServer($stockServer)
        ),
        $socket,
        $loop
    );
    
    // Subscribe to Redis
    $redisFactory = new RedisFactory($loop);
    $redisFactory->createSubscriber('127.0.0.1:6379')->then(function ($redis) use ($stockServer) {
        $redis->psubscribe('stock-opname.*');
        $redis->on('pmessage', function ($pattern, $channel, $message) use ($stockServer) {
            $stockServer->broadcast($message);
        });
    });
    
    // Run the loop
    $loop->run();
    

    This example:

    3. Verify Laravel Redis Configuration

    Ensure that Laravel and your WebSocket server are using the same Redis configuration (host, port, database index):

    Check in Laravel: config/database.php

    'redis' => [
        'client' => 'phpredis',
        'default' => [
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'port' => env('REDIS_PORT', 6379),
            'database' => env('REDIS_DB', 0),
        ],
    ],
    

    Ensure that REDIS_HOST, REDIS_PORT, and REDIS_DB match what the WebSocket server is using.

    Checklist