Laravel comes with a good implementation of WebSocket. It’s easy to broadcast informations for the users, and make communication between clients. You could simply write a good real-time web application using these features. Let's see how!
The problem
Originaly the WebSocket protocol is based on full-duplex communication channels. Laravel only implements message delivery from server-to-client. There is no way to send data from client-to-server through the WebSocket Server. Nevertheless, this featureless make the framework more secure: if the code written at client-side is not well accomplished some security and server-balance issues could come up. That’s why you should be very careful when you work on a client-to-server data sending.
What we have
When we are broadcasting the data is published to a Redis Channel, the Socket server subscribed to that, and listenging for any changes. If something happend the Socker server will send the data for the client side.
The concept
In the first view it’s seems like Laravel only implements the server-to-client communication, but this is half true. There is the whispering function, which is used for client-to-client message sending. The Socket server process the data and send to the other client, who are also joined to the channel.
We can grab this function and extend with our solution. When the Socket server emits the data for the clients, it also publish to Redis channel. Later we should write a cli command, which is subscribed for the specific channel and process the message correctly.
The implementation
We will use the Laravel Echo & Redis & Laravel-Echo-Server trio. Although we will use a fork of Laravel-Echo-Server what is capable to publish data to the Redis channel: when the Echo whispering method called, the Socket Server not just forwarding the message for another clients the data will be also published to the specific Redis channel (same as which it’s reading).
After you downloaded and configured the server start it:
node bin/server.js start
On the client side we should implement the whispering:
Echo.join('orderTable.' + orderId).whisper(
'OrderUpdated',
{
message: 'This message will be available from the server.'
}
);
Don’t forget: the channel should be presence or private channel, so you will be able to use the whispering function. Now, we should write the command, which is subscribed to our channel. First of all make the class:
php artisan make:command RedisSubscriber
Change the signature for something else:
protected $signature = 'redis:subscribe';
According the Laravel documentation we could subscribe for channels using wildcard. This is useful in this situation, because Laravel and Socket server are using prefixes in the channel’s name for better isolation and recognition when multiple app using the same Redis server. And also we adding postfixes to separate our channels from each other (based on the app’s business logic). Knowing these information in the handle() method we could subscribe for the channel:
Redis::psubscribe(['*orderTable.*'], function ($message, $channel) {
// do something
});
When you run your command after some time you could run into this error:
read error on connection to 127.0.0.1:6379
This error will be thrown by PHP after 60 seconds for closing the connection with the Redis server. You could manually change the default timout:
ini_set("default_socket_timeout", -1); //-1 is not set
Finally, this is how our Redis Subscriber should look like:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
class RedisSubscriber extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'redis:subscribe';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
ini_set("default_socket_timeout", -1);
Redis::psubscribe(['*orderTable.*'], function ($message, $channel) {
echo $channel;
echo PHP_EOL;
echo $message;
echo PHP_EOL;
});
}
}
We hope we could help you out. Read our other blog posts for similar content!
The original article is available here.