Real-time broadcasting integration in Laravel – Part 1

Complex projects usually require certain new elements to be integrated into the customized platform in order to make it unique and for the solution to match up to the client’s expectations.

As part of a recent web application development project, software experts at LiteBreeze implemented a real-time auction-bidding activity. A new feature that was added to enable this activity was ‘broadcasting’ using Laravel 5.4, Redis, Node.js and Socket.io.

Broadcasting here means the ability to update user interfaces real-time when any new activity happens elsewhere on the server. This is implemented using the Websocket API, which is a more efficient alternative to other available options.

Our implementation enabled any new bidding activity on the site to be simultaneously broadcast to all current users browsing the biddable auction pages. The current user’s bidding status (highest bidder, outbid, won or lost), highest bid amount and other additional user information are updated in real-time without a page reload.

The requirement that multiple AWS EC2 instances of the application may be deployed at any given time (autoscaling), added to making this integration a challenge. The broadcast data can be generated from any server instance, and consumed on a different server instance. We handled the challenge by using an independent Redis server instance for storing the broadcast data.

Laravel has several ingenious features to help implement broadcasting. The generic flow of events/data during the broadcasting process can be broken down as follows:

  • A Laravel event is fired when a bidding activity occurs.
  • The Laravel event class pushes the data onto a Redis channel.
  • A socket server set up using Node.js and socket.io, listens to updates on the Redis channel.
  • Any updates on the channel are captured and broadcast by the socket server.
  • A socketio client script implemented on the bidding pages create a Websocket, connected to the socket server.
  • When new data is broadcast on the Websocket, the data is updated live on the bidding pages using frontend Javascript.

Below is a step-by-step description of how we implemented this feature in our project.

We can split the implementation into three main sections:

  1. Configure Redis and fire event
  2. Socket server to broadcast data
  3. Create Websocket and consume data

Configure Redis

Redis host, client and database detail is configured in the config/database.php file. The ‘redis’ key could be configured as follows:

'redis' => [
    'client' => 'predis',
    'default' => [
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'password' => env('REDIS_PASSWORD', null),
        'port' => env('REDIS_PORT', 6379),
        'database' => env('REDIS_DATABASE', 0),
    ],
],

Here, predis the PHP Redis client library is used to connect to the Redis host. A package for the same should be installed. Use the following command to install the predis library

composer require predis/predis

The configuration values are set from constants defined in the .env file; a sample of which can be found toward the end of this blog.

The config/broadcasting.php file is used to define the default broadcast driver to be used by the application. We’ll set that to ‘redis’.

'default' => env('BROADCAST_DRIVER', 'redis'),

Also, make sure that the following line is uncommented in the ‘providers’ array in config/app.php.

Illuminate\Broadcasting\BroadcastServiceProvider::class,

Fire event

When an action occurs on the frontend which requires broadcasting, we fire an event. This can be done by the following call:

event(new ActionEvent($actionId, $actionData));

The ActionEvent definition should implement the ShouldBroadcast interface, which helps push the data onto the Redis host. Once the event and redis config are setup, we can use the broadcastOn and broadcastWith functions of the ShouldBroadcast interface on the Event class to push the data on to the Redis host.

Our project implemented Laravel queues, and Redis was used as both the broadcast driver and the queue driver. Depending on the number of jobs pushed on the queue, there was a delay in processing the broadcast data pushed on the queue. The workaround was using the ShouldBroadcastNow interface instead of ShouldBroadcast. This interface ensured that the broadcast data job was executed immediately, without waiting for other jobs on the queue to be processed.

An example event class code would look as follows (/app/Events/ActionEvent.php):

<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;

class ActionEvent implements ShouldBroadcastNow
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $actionId;
    public $actionData;

    /**
     * Create a new event instance.
     *
     * @author Author
     *
     * @return void
     */
    public function __construct($actionId, $actionData)
    {
        $this->actionId = $actionId;
        $this->actionData = $actionData;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @author Author
     *
     * @return Channel|array
     */
    public function broadcastOn()
    {
        return new Channel('action-channel-one');
    }

    /**
     * Get the data to broadcast.
     *
     * @author Author
     *
     * @return array
     */
    public function broadcastWith()
    {
        return [
            'actionId' => $this->actionId,
            'actionData' => $this->actionData,
        ];
    }
}

When the event is triggered, the event class pushes the data (actionId, actionData) to the Redis server. We can monitor the data on the Redis server and check if entries with the key ‘action-channel-one’ exist. If yes, we are good to move on to the next section on setting up the socket server to broadcast data.

For reference purposes, I’m adding example values for .env constants used in the code above.

BROADCAST_DRIVER=redis 
REDIS_HOST= 123.12.12.12 //127.0.0.1 if localhost 
REDIS_PASSWORD=yourRedisPassword 
REDIS_PORT=6379 // Redis port number, by default it is 6379 
BROADCAST_PORT=3444 //Can be any port number not used by other processes 
PUBLISHER_URL=http://app.publisher // The URL at which Publishing Server can be accessed

Know more about this integration from our second and third parts of this series. Feel free to contact us if you are looking for a Laravel developer job in Kochi, India – or if you’re looking to hire Laravel developers.