Using Laravel's centrifugo driver for broadcasting

Introduction: Popular Laravel Drivers and Their Issues

Modern web applications use web sockets, which are used to create user interfaces that are updated in real time. In case the data is updated on the server, then usually a message, notification is sent through the WebSocket connection for processing by the client. Instead of constantly, in a loop, monitoring the server part of the application for updates and changes in data that should be reflected in your user interface, you use the Websocket connection as an effective alternative. Today, websockets are sometimes not just an alternative, but a necessity for this type of tasks.

To help you develop features like these, Laravel makes it easy to broadcast server-side events over a WebSocket connection. When you broadcast your Laravel events, you can use the same event names and data between your client-side JavaScript application and your server-side Laravel application.

When you first encounter the need to provide your Laravel application with a real-time messaging and work system, you start with the broadcasting documentation. It becomes clear from there that the list of drivers specified in the documentation is not that wide, by default Laravel contains two server broadcast drivers to choose from: Pusher Channels and Ably. The main providers are paid. There are alternatives, but they are largely tied to the pusher protocol and/or Laravel Echo. Searching for other alternatives leads to approximately this incomplete list:

Using paid Pusher analogues is not in the plans (Pie Socket), some services, for example, Cowboy, are filtered due to weak support and unclear integration.

pusher, socket.iosockets can connect via the Laravel community-supported Echo, you get information about these tools right from the documentation page, so your eyes are drawn to them first, as was the case for me.

However, already at the first stages of integration (for 2022-2023), compatibility issues between Laravel Echo and new versions were discovered. socket.iowhich eventually led to the choice Laravel Websockets
So, Laravel Echo only worked with "socket.io-client": "^2.4.0"versions 3.4 either resulted in errors or did not connect to the server and did not transmit events. Details can be found here:
Laravel Echo Server, Redis, Socket.IO: Can't seem to make them work
Laravel Echo Server, no listening to channels but works with events
Not receiving websocket's message in frontend by the listener
In this discussion Socket.IO v3.0 support the authors clearly responded that “We decided that we would not waste any more time on Socket.ioas we do not support the laravel-echo-server package. Instead, we recommend using something like https://github.com/beyondcode/laravel-websockets . Sorry about that and thank you for understanding.”

Besides, before the introduction of Reverb, the Laravel Echo library was somewhat abandoned and development was inactive. And only with the advent of Reverb, the authors resumed active development. So much so that all issues and Pull requests were immediately closed. You don't see this every time. I would like to believe that it is reliable, and not hastily done.

As it turns out, compatibility issues can be resolved. On the front-end client side, pusher, socket.iosockets you can use it without laravel echo, with official clients:
pusher

var pusher = new Pusher("APP_KEY");
var channel = pusher.subscribe("APPL");
channel.bind("new-price", (data) => {
  // add new price into the APPL widget
});

socket.io

var socket = io(window.location.hostname + ':6001/', { transports: ['websocket'], });

socket.emit('subscribe', {
    channel: 'live',
    // auth: options.auth
}).on('live-event', function (channel, data) {
    console.log(data);
});

socketi uses the same Pusher SDK client

const PusherJS = require('pusher-js');

let client = new PusherJS('app-key', {
    wsHost: '127.0.0.1',
    wsPort: 6001,
    forceTLS: false,
    encrypted: true,
    disableStats: true,
    enabledTransports: ['ws', 'wss'],
});

client.subscribe('chat-room').bind('message', (message) => {
    alert(`${message.sender} says: ${message.content}`);
});

And this is a client-side solution. But if you run into problems and want a completely Laravel Echo-independent configuration, on the server side socket.io and socketi require installation and configuration of their own server on node js. That is, you will need to configure and keep the node js server up to date, plus the websocket server itself.

For 2022-2023, the author of the article gave preference Laravel Websockets server with the official Pusher client. The library is quite well-known, on Habr, for example, there is an article dedicated specifically to the issue of abandoning Pusher in favor of Laravel Websockets
However, this repository was archived by the owner on February 7, 2024. It is now read-only. The last release was in August 2023, since then there are many bugs related to incorrect dependency versions.

The package has its own shortcomings related to performance, because the server runs on php. It requires installation of numerous dependencies. Looking into composer.json, we see a wide list of them:

"clue/redis-react": "^2.6",
"guzzlehttp/psr7": "^2.6",
"illuminate/console": "^10.47|^11.0",
"illuminate/contracts": "^10.47|^11.0",
"illuminate/http": "^10.47|^11.0",
"illuminate/support": "^10.47|^11.0",
"laravel/prompts": "^0.1.15",
"pusher/pusher-php-server": "^7.2",
"ratchet/rfc6455": "^0.3.1",
"react/promise-timer": "^1.10",
"react/socket": "^1.14",
"symfony/console": "^6.0|^7.0",
"symfony/http-foundation": "^6.3|^7.0"

Time passed and I was faced with work tasks again, which brought me back to the topic of websockets and processing responses and requests in real time. Due to the fact that Laravel Websockets was outdated and archived, there was a need to integrate a new tool. It became clear that “goodbye” would have to be said to Laravel Websockets, and a new modern, open source alternative was needed. Initially, I considered the issue of least resistance, which is officially supported by the community and has a ready-made integration model.

I looked at the beautiful one Reverb presentation at LARACON EU 2024. Development (first commit) started more than two years ago, it has become more active recently and Reverb has good advertising support from the official Laravel team. But I decided, if I'm going to invest my time in learning and integrating, then why should it be Reverb, and not centrifugo, which I got acquainted with and became interested in several years ago, but never got around to integrating.

Integration of centrifugo server and Laravel driver

Centrifugo Server. Executable file

Centrifuge – is a program written in Go, a real-time message server. The server maintains constant connections from application users and provides an API for instant sending of any notification to active users subscribed to the notification channel. It can be used to create chats, “live” comments, multiplayer games, stream data and metrics (for example, rapidly changing exchange rates).
More details about the essence and advantages of this server are described by its creator in Centrifugo – 3.5 million revolutions per minute

My local computer runs Manjaro Linux, an Arch-based distribution. The official repositories of these distributions and aur directories do not contain the centrifugo package, so I manually downloaded and configured it from github section “releases”. The authors provide package options for most modern platforms and architectures:

  • Linux 64-bit Debian-based (amd64.deb, x86_64.rpm),

  • MacOS (darwin_amd64.tar.gz),

  • FreeBSD (freebsd_amd64.tar.gz),

  • Linux 32-bit (linux_386.tar.gz),

  • Linux 64-bit (linux_amd64.tar.gz),

  • Linux ARM 64-bit (linux_arm64.tar.gz),

  • ARM v6 (linux_armv6.tar.gz),

  • Windows (windows_amd64.zip)

The advantage of this approach is that you always have the latest build. The disadvantage is that you need to monitor the package update yourself, but I will tell you how to solve this issue below. I use the linux_amd64.tar.gz version.

How does the server program work?

After downloading the package to the desired folder and unzipping it, we have a statically compiled binary file centrifugo ready to run. You need to give it execution rights. Just one program file and no dozens of dependencies in composerwhich you need to monitor for updates, as was the case in beyondcode/laravel-websockets. No dependencies in the form of a node js server.

Centrifugo requires a configuration file with several secret keys, and authorization is required before connecting. There is a genconfig command that generates a minimal configuration file to get started:

./centrifugo genconfig

In this case, a configuration file config.json is created in the current directory (by default) with some automatically generated parameter values. You can make a config that does not require authorization, but this is unsafe and only for testing purposes. After that all you need to do is just run the executable file and the server will start working. This go program is your main real-time message server that starts waiting for commands from the api.

centrifugo --config=config.json

# 2024-07-02 14:32:07 [INF] starting Centrifugo engine=memory gomaxprocs=12 pid=81478 runtime=go1.22.3 version=5.4.0
# 2024-07-02 14:32:07 [INF] using config file path=/home/yoda/config.json
# 2024-07-02 14:32:07 [INF] enabled JWT verifiers algorithms="HS256, HS384, HS512"
# 2024-07-02 14:32:07 [INF] serving websocket, api endpoints on :8000

This is the server itself in its pure form. Now our main task is to send correctly prepared commands to the api, centrifugo will send messages in response.

All the magic of work based on the familiar REST Apiso we can work with the application as with any other HTTP API that we encounter in practice. It is possible to work with the GRPC API, but it is beyond the scope of this article.

Server program management. HTTP API

HTTP API — This is the easiest way to interact with Centrifugo from the backend of your application..

Centrifugo's HTTP API is running on the path /api (default).
HTTP API endpoint:

http://localhost:8000/api

The request format is very simple:

  1. This HTTP POST request to a specific API method path

  2. authorization data is specified via header Authorization: apikey <YOUR_API_KEY>

  3. The body of the post request (parameters) is sent as a JSON object.

in details: https://centrifugal.dev/docs/server/server_api#http-api

Scheme from the official website

Scheme from the official website

By default, all endpoints run on port 8000. This can be easily changed through the program config. port:

{"port": 9000}

now the api will be available at this address:

http://localhost:9000/api

The server API supports the following methods:

  • publish

  • broadcast

  • subscribe

  • unsubscribe

  • disconnect

  • refresh

  • presence

  • presence_stats

  • history

  • history_remove

  • channels

  • info

  • batch

Please note that some methods are disabled by default, such as presence_stats, history. When trying to access:

http://localhost:8000/api/presence_stats

You will encounter an error:
not available

Code:    108
Message: "not available"

The “Not available” error means that the resource is not enabled.

For example, this might be returned when attempting to access history or presence in a channel that is not configured to use history or presence features.

If you want to use these methods in the centrifugo config file, you need to set, for example, the following values:

"history_size": 20,
"history_ttl": "6s",
"presence": true,

Let's get back to the main functions.
Example of calling the publish method, by default:

http://localhost:8000/api/publish

Here is an example of posting a message to Centrifugo using a raw request from curl:

curl --header "X-API-Key: <API_KEY>" \  --request POST \  --data '{"channel": "chat", "data": {"text": "hello"}}' \  http://localhost:8000/api/publish

The purpose of this article is to show the use of centrifugo in Laravel, we, of course, do not plan to make raw queries, but more on that later. Now the main thing is that the interaction is very simple – it is the path to the method and the post request.

If the publication is successful, you will receive the following response:

{"result": {}}

This answer is with default settings, it differs when you configure presence settings, which are disabled by default.

It is especially worth paying attention tothat from version 5 centrifugo switches to a new api format. The request formats for versions up to and including 4 and 5 and further have differences.
Old HTTP API format is DEPRECATED

It is recommended to use the new HTTP API format instead of the old one whenever possible. The old format still works and is enabled by default. But the authors plan to eventually migrate the API libraries to the new format and then remove the old format. If you are using one of the official HTTP API libraries, a new version will be released at some point that will smoothly migrate you to the modern HTTP API format.

By the way, the author of the article has already sent a pull request to the official PHP client and it was successfully accepted.
centrifugal v5 new HTTP API version support
So version 5 and the new api format is not a problem for php clients now.

If earlier, up to and including version 4, the request format was:

curl --header "Authorization: apikey <API_KEY>" \
  --request POST \
  --data '{"method": "info", "params": {}}' \
  http://localhost:8000/api

i.e. the path to the method was specified in the data attributes of the post request – --data '{"method": "info", "params": {}}'.
In version 5, the authors of centrifugo decided to unify requests to the API with standards:

curl --header "X-API-Key: <API_KEY>" \
  --request POST \
  --data '{"channel": "test", "data": {"value": "test_value"}}' \
  http://localhost:8000/api/publish

method was removed from data attributes. Method is now part of the link (path)

was

became

--data '{"method": "publish"}'

http://localhost:8000/api/publish

In addition, changes have been made to authorization.

Before version 4, the HTTP authorization header format was:

X-API-Key: <YOUR_API_KEY>

in version 5 it was changed to:

Authorization: apikey <YOUR_API_KEY>

PHP SDK for centrifugo and Laravel driver

For the broadcast we will use the Laravel driver, it helps to simplify the interaction between Laravel and Centrifugo, providing convenient shells. The Laravel driver is a high-level shell for the PHP SDK. It is needed so that we I didn't have to make requests directly from the client php class and we could use Laravel's native broadcast methods. Laravel makes it easy to “broadcast” server events via a WebSocket connection and using generic methods, regardless of the driver vendor.
The basic concepts of broadcasting are simple: clients connect to named pipes on the frontend, while your Laravel application broadcasts events to those pipes on the backend. These events can contain any additional data you want to make available to the frontend.

Official php client

PHP library for communicating with Centrifugo's HTTP API
centrifugal/phpcent
it's just a client Centrifugo HTTP API Clientnot a driver for Laravel.
It can be used as standalone tool for interacting with api.

$client = new \phpcent\Client("http://localhost:8000/api");
$client->setApiKey("Centrifugo API key");
$client->publish("channel", ["message" => "Hello World"]);

Things described in the Laravel Broadcasting section of the documentation won't work with it. It needs to be built in.

Full-featured drivers for Laravel Broadcasting from the community

As mentioned earlier, by default Laravel contains two server broadcast drivers to choose from: Pusher Channels and Ably. Support for other providers can be obtained by writing a driver yourself, or if the provider provides it. For centrifugo Laravel, the provider does not provide drivers, but there are community-written ones. By the way, the official website states that

Centrifugo integration can be done in a few steps even without third-party libraries using the integration guide. Direct integration can allow using all Centrifugo features without restrictions that may be introduced by a third-party shell.

This is of course not entirely true, because with “direct integration” you will not be able to fully use the broadcast of events in Laravel.
For example, this won't work with “direct integration”:

$order = Order::create([]);

OrderShipmentStatusUpdated::dispatch($order);

You will need to directly call methods of your own class or raw http requests to send data, as well as manually send them to queues for delayed sending, authorize private channels, etc. things that, when using a driver, are done and written once and are executed “under the hood”.

Review and study of the information allowed us to form the following list of Laravel drivers for centrifugo:

  1. LaraComponents / centrifuge-broadcaster https://github.com/LaraComponents/centrifuge-broadcaster The progenitor of all the others presented below.

  2. Opekunov / laravel-centrifugal-broadcaster https://github.com/Opekunov/laravel-centrifugo-broadcaster

  3. denis660 / laravel-centrifugal https://github.com/denis660/laravel-centrifugal

  4. alexusmai / laravel-centrifugal https://github.com/alexusmai/laravel-centrifugal

4 packages that are very similar in implementation. The implementation contains

HTTP API client, which is largely similar to the original centrifugal/phpcent . With the difference that centrifugal/phpcent no installation required GuzzleHttp\Client and uses the native php module “ext-curl”.
On the one hand, it was possible to add a dependency as an HTTP API client and not write your own client. On the other hand, we get an independent package if centrifugal/phpcent will stop developing, the presented drivers do not depend on it.
None of the packages have support for Laravel 11.

Actually, this driver implements most of the Centrifugo API methods needed by Laravel for broadcasting. They are implemented through the well-known GuzzleHttp\Client

The Centrifugo client (class) can also be used separately from the driver.

A simple example of using the client:

<?php
declare(strict_types = 1);

namespace App\Http\Controllers;


use denis660\Centrifugo\Centrifugo;
use Illuminate\Support\Facades\Auth;

class ExampleController
{

    public function example(Centrifugo $centrifugo)
    {
        // Отправить сообщение в канал news
        $centrifugo->publish('news', ['message' => 'Hello world']);

        // Сгенерировать токен для подключения
        $token = $centrifugo->generateConnectionToken((string)Auth::id(), 0, [
            'name' => Auth::user()->name,
        ]);

        // Сгенерировать токен для подключения к приватному каналу
        $apiSign = $centrifugo->generatePrivateChannelToken((string)Auth::id(), 'channel', time() + 5 * 60, [
            'name' => Auth::user()->name,
        ]);

        // Получить список активных каналов
        $centrifugo->channels();

        // Получить информацию о канале news, список активных клиентов
        $centrifugo->presence('news');

    }
}

however, the driver documentation does not provide information on how to authenticate and obtain a token using Laravel's built-in tools. But generating an Initial Connection Token is, by default, a mandatory condition for connecting to the centrifugo server.
The token is provided as follows:

const client = new Centrifuge('ws://ваш.сайт/connection/websocket', {
    token: 'JWT-GENERATED-ON-BACKEND-SIDE'
});

If you leave the token property empty, the server will return a “client credentials not found” error and will not allow the client to connect:

{"level":"info","client":"38ed3443-91fc-4100-9581-704012710fe1","user":"","time":"2024-06-09T15:00:06+03:00","message":"client credentials not found"}

cm. Client SDK API

Client and server

But how does it happen that both a js application and a php application in terminology are clients?

After startup, centrifugo listens (waits) for connections on port 8000

Proto    Local Address     Foreign Address         State       PID/Program name          
tcp      0.0.0.0:8000      0.0.0.0:*               LISTEN      81478/centrifugo       

js client in Laravel applications, as a rule, does not communicate with the centrifugo api client directly (although this is possible).
js client connects not to api, but to websocket and creates a channel.

websocket access point
on the local development computer
ws://localhost:8000/connection/websocket
on the production site
ws://ваш_сайт/connection/websocket
The request from the client browser to the address ws://your_site/connection/websocket is usually proxied through your web server, for example, nginx. This needs to be configured, more on that later. Therefore, during the initial setup, it is important not to confuse and not to do as indicated in all the documentation and help for the js client new Centrifuge('ws://localhost:8000/connection/websocket'). This address is for local development only.if you leave it unchanged, when the production server returns the page to the user's browser, it will have a request for localhost. The user's browser will knock on its own local computer and connections will not go through. When you configure many system components, this can be missed.

Our php Laravel application is a server for the js application in the browser, which receives and processes, sends messages. At the moment when the php application receives a request from js, it, in turn, sends a request to the centrifugo application via http api. And in this sense, the php application (sdk, Laravel driver) is a client of the centrifugo api.

php gives commands and sends messages via http api
default access point
http://localhost:8000/api

Accordingly, one controls and sends, the other listens and receives. php sends to the channel, js receives from the channel. So php is not exactly a client, it is a backend for managing centrifugo api, the client is a js application in the browser. There are other configuration options, but this is the main one.

Before a client can connect to a server, it must be authorized.
js client is authorized by receiving a jwt token from the server. Since we agreed that this server is a php application, it should issue this token.

Accordingly, php needs to generate a login using the api method and provide it to the js client.

Connecting and Configuring the Laravel Driver

Install the driver via composer.

Open yours config/app.php and add the following to the section:

<?
return [
    'providers' => [    
        App\Providers\BroadcastServiceProvider::class,
    ],  
];

Open your config/broadcasting.php and add a new connection there:

<?
return [
        'centrifugo' => [
            'driver' => 'centrifugo',
            'token_hmac_secret_key'  => env('CENTRIFUGO_TOKEN_HMAC_SECRET_KEY',''),
            'api_key'  => env('CENTRIFUGO_API_KEY',''),
            'url'     => env('CENTRIFUGO_URL', 'http://localhost:8000'), // centrifugo api url
            'verify'  => env('CENTRIFUGO_VERIFY', false), // Verify host ssl if centrifugo uses this
            'ssl_key' => env('CENTRIFUGO_SSL_KEY', null), // Self-Signed SSl Key for Host (require verify=true)
        ],    
];

You should also add these two lines to your .env file:

CENTRIFUGO_TOKEN_HMAC_SECRET_KEY=token_hmac_secret_key-from-centrifugo-config
CENTRIFUGO_API_KEY=api_key-from-centrifugo-config

These lines are optional:

CENTRIFUGO_URL=http://localhost:8000
CENTRIFUGO_SSL_KEY=/etc/ssl/some.pem
CENTRIFUGO_VERIFY=false

Don't forget to change the parameter BROADCAST_DRIVER in the .env file!

BROADCAST_DRIVER=centrifugo

Sample Centrifugo server configuration file (not to be confused with config/broadcasting.php):

{
    "token_hmac_secret_key": "ключ",
    "admin_password": "пароль",
    "admin_secret": "секрет",
    "api_key": "ключ",
    "allowed_origins": [
        "http://127.0.0.1",
        "http://localhost",
    ],
    "admin": true,
    "client_connect_include_server_time": true,
    "allow_subscribe_for_client": true,
    "history_size": 20,
    "history_ttl": "6s",
    "presence": true,
    "log_level": "debug",
    "address": "127.0.0.1"
}

Backend websocket server Laravel

The question arises when is it better to generate and transfer authorization parameters?

  • can be generated in middleware

  • you can send a request by first creating a route in broadcast to generate a token

  • generate when you log into your account on a permanent basis

  • you can issue a token for a web socket along with authorization in your application account – authorize, save the token in the session, regenerate under certain conditions

<?

class AuthService
{
 public function generateConnectionToken(User $user): string
    {
        /* @var  CentrifugoInterface $cenrifugo */
        $cenrifugo = \app()->make('centrifugo');

        $token = $cenrifugo->generateConnectionToken((string) $user->id);

        session(['jwt_centrifugo_token' => $token]);

        return $token;
    }
}

route

<?

Route::middleware(['auth'])
    ->group(function () {
    
    Route::get('item', 'index')->name('item');
   //... другие роуты
});

controller

<?
class ItemController
{
    public function index()
   {
      return response()->json([
        'token'=>session('token'),
      // ... другие данные
      ]);
   }
}

It would be better not to pass the token in the controller every time, but to make a separate middleware, and share the token data with the necessary controllers. Or do it in the boot method app/Providers/AppServiceProvider.php via View Share.

The token will need to be updated if the centrifugo config or key has changed. CENTRIFUGO_TOKEN_HMAC_SECRET_KEY

Token is access to the centrifugo message server. To use a real-life example. It's like a student ID at a university. Once you've got it, you can pass the security guard and walk around the territory freely. However, such access does not give you the right to attend other people's lectures, enter the dean's office or the department.
It is more convenient to handle this access via channels through the Laravel framework itself, for example, like this:

<?
use App\Models\User;

Broadcast::channel('educational department', function (User $user, ?int $certificateId) {
    return $user->status->name === 'teacher' && !empty($certificateId);
});

If the result of this closure === true, the user's js client is authorized in the channel and can listen to messages from it.

It is also possible via API client

<?
// Сгенерировать токен для подключения к приватному каналу
$apiSign = $centrifugo->generatePrivateChannelToken(
  (string)Auth::id(), 'channel', time() + 5 * 60, [
            'name' => Auth::user()->name,
]);

If you did something wrong, you can see the error codes here:
Client protocol codes

Connecting to centrifugo via js client

Now your users can start connecting to Centrifugo. You need to get the client library centrifuge-js for your application interface.

centrifuge-js is the official SDK real time and complies with the centrifugo specification. Provides a client for connecting to Centrifuge or anyone Centrifuge-based server using pure WebSocket or alternative transports (HTTP streaming, SSE/EventSource, experimental WebTransport) from a web browser, ReactNative or NodeJS environments.

The js client currently has the widest range of features. Among clients in other languages, centrifuge-js stands out for its additional features, namely:
in functions related to

  • connection – batching API, bidirectional WebSocket emulation,

  • Client-side subscription – Optimistic subscriptions, delta compression. centrifuge-js uses JSON serialization for protocol frames by default. This makes communicating with the Centrifugo server convenient, since we exchange human-readable JSON frames between the client and the server. And this makes it possible to use centrifuge-js without an additional dependency on the protobuf.js library.

Websocket — the primary transport in Centrifugo. It is a very efficient, low-overhead protocol on top of TCP.

The biggest advantage is that Websocket works out of the box in all modern browsers, and almost all programming languages ​​have Websocket implementations. This makes Websocket a universal transport that can be used to connect to Centrifugo from almost anywhere. But if you are not satisfied with it, there are others.

The default WebSocket connection path in Centrifugo is:

/connection/websocket

Each client must provide a connection token (JWT) when connecting. You must generate this token on your server side using the Centrifugo secret key.which you set in the server-side configuration (note that in the case of RSA tokens, you generate a JWT with a private key). For information on how to generate this JWT, see in a special chapter .

You pass this token from the server side to your front-end application. (pass it in the template context or use a separate request from the client side to get the user's JWT from the server side). And use this token when connecting to Centrifugo (for example, in the browser client there is a special method setToken).

If you pass the authorization token incorrectly, you will receive the following error code:
Unauthorized

Code:    101 
Message: "unauthorized"

The Unauthorized error indicates that the request is unauthorized.
For initial setup and debugging, it is better to put in the centrifugo config file

"log_level": "debug",

Then in the centrifugo application logs you will be able to see more information about whether connections are successful, messages and error details.

Connect to your local Centrifugo using the JavaScript SDK:

const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket', {
    // token: ?,
});

client.connect();

Note that we explicitly call .connect() method for initiating a connection to the server.
It is not necessary to connect to the server immediately. You can do this conditionally, and most importantly, you can pre-configure all stages of connection, notifications and subscriptions. Configure the object itself that we saved in the variable centrifuge.

Example if you are using vue.

import { Centrifuge } from 'centrifuge';
import { reactive } from "vue";

export function useCentrifugo(endpoint = "ws://ваш.сайт/connection/websocket", options) {
    const centrifugo = new Centrifuge(endpoint, options)

	// Клиентские методы и события. Настраиваем, что делать в случае началы соединения, успешного соединения, разрыва связи 
    centrifugo.on('connecting', function (ctx) {
        console.log(`connecting: ${ctx.code}, ${ctx.reason}`);
    }).on('connected', function (ctx) {
        console.log(`connected over ${ctx.transport}`);
    }).on('disconnected', function (ctx) {
        console.log(`disconnected: ${ctx.code}, ${ctx.reason}`);
    });

    // Настраиваем подписки, их например, можно  передать в аргументах options?.channels, при создании объекта 
    const subscriptions = reactive({})

    if (options?.channels) {
        for (let i = 0; i < options.channels.length; i++) {

            const channel = options?.channels[i];

            subscriptions[channel] = centrifugo.newSubscription(channel);

            subscriptions[channel].on('publication', function (ctx) {
                console.log(ctx);
            }).on('subscribing', function (ctx) {
                console.log(`subscribing: ${ctx.code}, ${ctx.reason}`);
            }).on('subscribed', function (ctx) {
                console.log('subscribed', ctx);
            }).on('unsubscribed', function (ctx) {
                console.log(`unsubscribed: ${ctx.code}, ${ctx.reason}`);
            })
        }
    }

    return { centrifugo, subscriptions }
}

Wherein subscriptions[channel].on you can add, rewrite and more. After the initial setup and launch of the function:

const websocket = useCentrifugo("ws://ваш.сайт/connection/websocket", options);

websocket.centrifugo.on('disconnected', function (ctx) {
    // новый код;
})

websocket.subscriptions['ваш канал подписки'].on('disconnected', function (ctx) {
    // новый код;
})

A more detailed example:

const { centrifugo, subscriptions } = useCentrifugo(
    pageData.value.props.config.ws?.route,
    pageData.value.props.config.ws
);

centrifugo.on('connected', function (ctx) {
    subscriptions['ваш канал подписки'].on('publication', function (sub) {
        if (sub.data.event == 'markedCreated') {
          
          // показываем модальное окно
            showModal(sub.data.item)
            
        }
    }).subscribe();

  // Подписываемся только, если в админке включен этот канал и если он доступен. Иначе будут постоянные ошибки подключения
    if (pageData.value.props.config.live !== undefined && pageData.value.props.config.live.status) {

        subscriptions.live.on('publication', function (sub) {
            // console.log(sub)
        }).subscribe();

    }
});

As you can see in the example, we use the event name check.

if (sub.data.event == 'markedCreated')

This is an alternative listen method if you have used Laravel echo before:

Echo.channel('канал подписки')
    .listen('markedCreated', (e) => {
    // показываем модальное окно
    showModal(e.item)
});

By default, the client can only listen to these subscription events:

  • publication – called when a new publication is received from a subscription channel

  • join – called when someone joins the channel

  • leave – called when someone leaves the channel

  • subscribing – called when the subscription is switched to subscribing state (initial subscription and resubscription)

  • subscribed – called when the subscription is switched to subscribed state

  • unsubscribed – called when the subscription is switched to unsubscribed state

  • error– called when a channel subscription failed. It can be called multiple times during its lifetime, since the browser client automatically resubscribes to channels after a successful reconnection. (e.g. due to a temporary network outage or a Centrifugo server restart)

Events you send from Laravel are not automatically recognized by it.

In your Laravel application, to pass a message you do something like this:

<?

class MarkedCreated extends Event implements ShouldBroadcast  
{  
    use InteractsWithSockets, SerializesModels;  
  
    public function __construct(public Item $item)  
    {               
    }   

    public function broadcastOn(): Channel
    {
        return new Channel('marked');
    }

    public function broadcastAs(): string
    {
        return 'markedCreated';
    }

}

Instead of a conclusion.

Thanks to everyone who read to the end. I hope this article will be useful to everyone who is looking for a new way to work with websockets in Laravel and wants to expand their skills.
In the future, I plan to expand the content of this article with a second part, where I will tell you how to write a systemd service for automatically starting centrifugo, ansible scripts for automatic publishing on the server, and a script for automatically updating the centrifugo version using github repositories.
In addition to the advantages in the form of performance, ease of setup, functionality presented in this article, it is nice that the project is open-source and was created by our fellow compatriot and his team.
If you are interested in the driver for Laravel 11 and support for the new api version 5 centrifugo I made a fork and added these functions, I use it myself in my work Laravel driver Centrifugal 5

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *