Laravel-websockets: Bi-directional broadcasting with Laravel echo

Created on 8 Jun 2019  Â·  20Comments  Â·  Source: beyondcode/laravel-websockets

I have a server configured for multi tenancy, I can broadcast events with private channels from my applications to front end clients using the Laravel echo client to listen for events but I actually wish to emit events from the front end too, to trigger events on back end.

Is this possible with this package or is this strictly server to client broadcasting only?

enhancement good first issue

Most helpful comment

Just a random thought, but this could be possible if the package was able to send jobs to the queue. The real work would be done in a worker with full access to Laravel, the package would only receive incoming WebSockets messages (which it already does for pings & whispers).

Queue connections would have to be async, though. I know there are ReactPHP clients for Redis and it's apparently possible to have the AWS SDKs in the event loop, which would allow supporting SQS.

Also, Laravel Echo would have to be extended to support client->server messaging (besides whispers).

Something like this:

untitled

I know the idea is to be a replacement for Pusher, but this wouldn't break the protocol, it would extend it. Everything you can do with Pusher would still be possible, plus you would gain the ability to receive incoming messages.

Additionally, we could make use of the channel classes and add more methods to it, like:

<?php

namespace App\Broadcasting;

use App\Room;
use App\User;

class ChatChannel
{
    public function join(User $user, Room $room)
    {
        if ($room->wasBanned($user)) {
            return false;
        }

        return $user;
    }

    public function createMessage(User $user, $data)
    {
        $room = $user->joinedChats()->findOrFail($data['room_id']);

        $message = new Message(['content' => $data['content'], 'user_id' => $user->id]);

        $room->messages()->save($message);

        broadcast(new ChatMessage($message));
    }
}

In Echo, something like this would do the trick:

const channel = Echo.join(`chats.${roomId}`)
    .here((users) => {
        //
    })
    .joining((user) => {
        console.log(user.name);
    })
    .leaving((user) => {
        console.log(user.name);
    });

// when user sends a message:

channel.perform('createMessage', { room_id: roomId, content: 'Hello world!' });

The ChatChannel::createMessage method would be called in the worker, in this example.

The only "drawback" about the current broadcasting component in Laravel is that it doesn't make use of the bi-directional, full-duplex nature of WebSockets due to the Pusher protocol limitation.

I'm not sure if adding this would be that beneficial over AJAX, as it won't be real-time (as the message would have to go through a view hops to be handled and the side-effects to be broadcasted again). However, folks in the Rails world have done something similar with AnyCable but using gRPC instead of a queue.

Thoughts? Should I open a new issue for further discussion?

All 20 comments

This is definitely possible. Have a look at https://docs.beyondco.de/laravel-websockets/1.0/basic-usage/pusher.html#usage-with-laravel-echo and the Laravel docs on Laravel Echo. Good luck!

Thanks for your response Alex, however I still feel there is something missing. For example how do I make My Laravel application listen for the events from the client and handle them accordingly.

Apologies if I’m missing something obvious here, but I can’t find any useful documentation for this. Everything seems to be related to broadcasting server to client or client to client but not client to server

I'm using laravel echo to websocket the search, I need to inform the server of what the user is typing (and other cases, tables, that I need to send pagination info). So I really require this to procced.

@RafaelCorreaQuantik - did you find a solution to this ? I've heard others are using Redis to achieve this, but it's websockets right. Should be definitely allow for bi-directional communication. For now I've resulted to just using ajax for posting back to the server

@minimatrix - I found out about window.Echo.connector.socket.emit, couldn't test it right, and also. I don't know how to handle the server side of this request.

@RafaelCorreaQuantik This package is currently only showing to way to be a pusher replacement. It does not show any bi-directional communication example, though from the codebase it looks like possible.

I too was interested with the their __websocket in php, done right__ statement, but too bad it isn't have much information about it yet. If you have any clue please share it with us. Thank you.

@justinmoh, maybe this is a pusher limitation indeed. Changing from pusher to socket io would be really hard at this point for the package. But may the only option, since socket.io natively supports bi directional communication.

I've been investigating this window.Echo.connector.socket.emit implementation and found this issue by luck. Here are my final notes:

  • Socket.io's emit function can update Redis but the problem is the server implementations between client and backend. This is what I see here too. Echo and Laravel WebSockets are failing on the exact same subject.
  • Laravel Echo Server does not listen client events and they are not planning to do in future. (check this and many more issues about this subject are closed state now)
  • If Echo or Laravel Websockets implements this approach successfully, you may use Redis Pub/Sub listen and trigger events on server side.

Unfortunately there's a lot of reasons this is tricky to handle.

  • ReactPHP is an async event loop framework, meaning that any blocking IO will cause the websocket server to stall. This means any of the stock SQL or Redis connections/libraries used in Laravel can't be used from code that runs on the websocket server.
  • To get around this, you can use https://github.com/clue/reactphp-redis which is an async Redis client, which would let you communicate with Redis efficiently from within the websocket server.
  • But the problem is that the Laravel Redis queue driver doesn't support react-redis connections for pushing jobs onto the queue. This means you'd have to implement your own driver (or wait for someone else to write one - actually this is something that interests me, and I know how to do this, so I might write this as a library at some point in the future when I find some extra time)
  • You could go the SQL route with https://github.com/friends-of-reactphp/mysql but again, Eloquent doesn't support these kinds of connections, so you'd have to write raw SQL (not a deal breaker, just a bummer). Then you'd need to set up polling for changes in the DB if you want to actually do some other kind of IO.
  • etc.

All that to say, just use ajax. It's way easier, and it's a direct channel to your backend anyways. Ajax is very fast if you stick to small JSON messages, even better if you use HTTP/2 so it'll re-use the same TCP connection (I think).

I cannot give you code right now but I can give you the idea. Try to use a custom implementation of WebSocketHandler(find custom handler option in docs) and copy the complete code of WebsockerHandler.php of this package. In the onMessage() method you will be able to handle the incoming messages.

Don't forget to enable client messages option in config file.

Hope that helps.

Just a random thought, but this could be possible if the package was able to send jobs to the queue. The real work would be done in a worker with full access to Laravel, the package would only receive incoming WebSockets messages (which it already does for pings & whispers).

Queue connections would have to be async, though. I know there are ReactPHP clients for Redis and it's apparently possible to have the AWS SDKs in the event loop, which would allow supporting SQS.

Also, Laravel Echo would have to be extended to support client->server messaging (besides whispers).

Something like this:

untitled

I know the idea is to be a replacement for Pusher, but this wouldn't break the protocol, it would extend it. Everything you can do with Pusher would still be possible, plus you would gain the ability to receive incoming messages.

Additionally, we could make use of the channel classes and add more methods to it, like:

<?php

namespace App\Broadcasting;

use App\Room;
use App\User;

class ChatChannel
{
    public function join(User $user, Room $room)
    {
        if ($room->wasBanned($user)) {
            return false;
        }

        return $user;
    }

    public function createMessage(User $user, $data)
    {
        $room = $user->joinedChats()->findOrFail($data['room_id']);

        $message = new Message(['content' => $data['content'], 'user_id' => $user->id]);

        $room->messages()->save($message);

        broadcast(new ChatMessage($message));
    }
}

In Echo, something like this would do the trick:

const channel = Echo.join(`chats.${roomId}`)
    .here((users) => {
        //
    })
    .joining((user) => {
        console.log(user.name);
    })
    .leaving((user) => {
        console.log(user.name);
    });

// when user sends a message:

channel.perform('createMessage', { room_id: roomId, content: 'Hello world!' });

The ChatChannel::createMessage method would be called in the worker, in this example.

The only "drawback" about the current broadcasting component in Laravel is that it doesn't make use of the bi-directional, full-duplex nature of WebSockets due to the Pusher protocol limitation.

I'm not sure if adding this would be that beneficial over AJAX, as it won't be real-time (as the message would have to go through a view hops to be handled and the side-effects to be broadcasted again). However, folks in the Rails world have done something similar with AnyCable but using gRPC instead of a queue.

Thoughts? Should I open a new issue for further discussion?

Yeah your thoughts basically align with what I wrote above. We just need someone to write a queue driver that uses https://github.com/clue/reactphp-redis. The rest is basically a given once that exists. I've wanted to spend time doing that, but I don't particularly need this feature myself so I haven't yet.

I might find some time over the festive period to write this queue driver,
it’s something that I feel will add value to this project

On Sat, 21 Dec 2019 at 21:00, Francis Lavoie notifications@github.com
wrote:

Yeah your thoughts basically align with what I wrote above. We just need
someone to write a queue driver that uses
https://github.com/clue/reactphp-redis. The rest is basically a given
once that exists. I've wanted to spend time doing that, but I don't
particularly need this feature myself so I haven't yet.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/beyondcode/laravel-websockets/issues/190?email_source=notifications&email_token=AFUKRJRNRXEJQSHGTHBMKT3QZZ7VDA5CNFSM4HV65GTKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEHPDQ7Q#issuecomment-568211582,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AFUKRJTSPJ33G2LZ65RNQETQZZ7VDANCNFSM4HV65GTA
.

If you do, feel free to @ me for a code review 😄

Did anyone has implemented a clear solution for this issue yet?

We need to bring bi directional broadcasting with Laravel echo, we can currently do it with posting data with axios back to server and then doing a broadcasting on the backend but this doesn't work in realtime there is a small amount of delay in working of them, when using Socket.io with NodeJS backend everything works in real time and without delay, this might be also because of NodeJS it self, PHP takes time for excuetion than NodeJS. But if we have broadcasting or emmitting from the client it self then we can do it more productive way and the simplicity of the design would be maintained.

So as a general announcement, now beta, 2.x version does come with multi-node support for horizontal scaling thanks to @francislavoie <3 The underlying code uses ReactPHP Redis' implementation for non-blocking I/O, but I am not really sure

This deems to be a good idea for the Laravel package itself, as well as the laravel/echo repo, because it lacks the ability to emit custom events directly from the pusher provider in Echo client: https://github.com/laravel/ideas/issues/2350

The only workaround is to use a custom endpoint that will do that for you, like taking the body and handing it to the backend or via a queue message manager. @veermetri05 You can use client-to-client whispering instead of processing the backend over and over again, unless you need additional backend code to be ran.

Like I said earlier:

We just need someone to write a queue driver that uses clue/reactphp-redis.

If that's set up, then L-W can easily broadcast jobs to the queue using that driver for bidirectional messages to work.

@francislavoie @tonysm I have written the queue driver a while back, guess I'll have to push it and write tests for it.

@francislavoie @tonysm I have pushed the changes to https://github.com/beyondcode/laravel-websockets/pull/552, take a look whenever you can.

Was this page helpful?
0 / 5 - 0 ratings