beyondcode / laravel-websockets

Websockets for Laravel. Done right.
https://beyondco.de/docs/laravel-websockets
MIT License
5.08k stars 634 forks source link

Bi-directional broadcasting with Laravel echo #190

Closed minimatrix closed 10 months ago

minimatrix commented 5 years ago

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?

AlexVanderbist commented 5 years ago

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!

minimatrix commented 5 years ago

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

ghost commented 5 years ago

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.

minimatrix commented 5 years ago

@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

ghost commented 5 years ago

@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.

justinmoh commented 5 years ago

@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.

ghost commented 5 years ago

@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.

onurkose commented 5 years ago

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

francislavoie commented 5 years ago

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

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).

gagansandhu25 commented 5 years ago

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.

tonysm commented 4 years ago

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?

francislavoie commented 4 years ago

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.

minimatrix commented 4 years ago

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 .

francislavoie commented 4 years ago

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

imkonsowa commented 4 years ago

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

veermetri05 commented 4 years ago

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.

rennokki commented 4 years ago

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.

francislavoie commented 4 years ago

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.

rennokki commented 4 years ago

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

rennokki commented 4 years ago

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

jbardnz commented 2 years ago

I was wondering if anyone could clarify if this functionality is now possible? It seems like the prerequisites are done but I'm not sure if this feature was ever implemented?

kpebron commented 2 years ago

I recently was able to achieve bi directional communication with Laravel. Laravel Broadcasting for server to client communication while using websockets api for client to server communication. My SO answer: https://stackoverflow.com/a/74310959/13933721

DrjavaB commented 2 years ago

I'm not sure your code works @kpebron ?! can you explain how to listen redis for emitted messages from client and save them to database? (laravel-echo-server + socket.io-client) Screenshot from 2022-11-20 02-32-50

ernst77 commented 1 year ago

No progress?

Forsakenrox commented 1 year ago

No progress?

No progress. vervsion 2.x is outdated and no longer update. I think this because of swoole and laravel octane. Swoole has its own websocket server and does not have i/o and async issues, i think community must work on enchance swoole support in laravel octane to bring all swoole features to work, because now swoole in octane in very starting point.

kpebron commented 1 year ago

I'm not sure your code works @kpebron ?! can you explain how to listen redis for emitted messages from client and save them to database? (laravel-echo-server + socket.io-client) Screenshot from 2022-11-20 02-32-50

Not socket.io but websockets. socket.io has different setup (I think). I did this last year and kind of forgot how to do this again. besides this was more of a hack-ish kind of solution. It would be great if laravel-websockets could implement this naturally without complicated setup.

Eyad-Bereh commented 10 months ago

I'm stuck on the same issue

I understand that Pusher - as the name suggests - pushes from the server to the client. Still, my use case is different because I'm implementing an online auction system and the bids need to happen through a bi-directional connection since I can't fire an XHR request every single time someone makes a bid because it would be a complete kill, and when I say a "complete kill" then I mean it because I've created a script that simulates bidding operations and made it run and create thousands of virtual bids per second. I felt like I've DDoS-ed myself, so using XHR isn't an option at all in this case ...

And if I understand correctly, then Laravel doesn't seem to be able to listen to any client-side events, and at this point, I've started to question myself whether Laravel was the correct choice for this kind of project in the first place, but I don't know ...

francislavoie commented 10 months ago

@Eyad-Mohammed-Osama you just need to be using Laravel Octane, then the XHR requests to your server will have nearly zero overhead compared to a bidirectional connection. All of the overhead is from framework bootstrap, but that's completely avoided with Octane because it keeps a worker running to accept requests without redoing the bootstrap.

As I wrote above in this issue, I don't think people understand how much more complexity is added when involving bidirectional websockets. It's also risky to run your own code in reaction to incoming messages, unless you're careful to only use libraries/clients that are properly async. Any blocking IO would cause your WS server to stall until the IO is done, ruining performance.

Eyad-Bereh commented 10 months ago

@francislavoie Thanks for the suggestion. I'll definitely take my time to look more into Laravel Octane, it seems promising for my use case.

I understand your point about bidirectional web sockets and the complexity behind them. While I was working on the auction feature, I've already tried something like this:

image

I thought that maybe I could make a Node.JS server that proxies the incoming requests to the Laravel backend through some IPC mechanism, or maybe by passing the data through a Laravel console command, but this was painful to do for several reasons:

  1. The authentication flow for private and presence channels was very complicated because the user must send their bearer token to the socket server, and the socket server must proxy this to an authentication endpoint or a command in the Laravel project which checks whether a user is eligible to subscribe to the channel or not and send the result back to the Socket.IO server, and this was a whole another story because the async Socket.IO server will have to wait for the response to come back from the sync Laravel server
  2. I had many security and performance concerns

So I didn't do it because I couldn't take any chances. I may do this in my spare time as a side project, but not on a production project.

DrjavaB commented 10 months ago

You may use openswoole for bidirectional socket connection. But if you want to use Laravel you should use message broker such as rabbit or redis for connection between laravel and nodejs (or everything handles socket) In this case laravel echo uses redis as message broker but. It is fast but not safe!

imkonsowa commented 10 months ago

Guys, Laravel reverb has been announced and it seems to support client-to-server communications over websockets.

Eyad-Bereh commented 10 months ago

@DrjavaB That's right In my suggested architecture, I forgot to mention that I used Redis as a broker, but I agree with you that dealing with things like private channels and detecting whether a user is eligible to subscribe or not is a real hell, because I've been there and done that

Forsakenrox commented 10 months ago

Guys, Laravel reverb has been announced and it seems to support client-to-server communications over websockets.

Where did you read this ? reverb is actually laravel-websocket which reintegrated with additional pulse panel. they still use Pusher protocol which not support client-to-server data transfer. Don't mislead people with this information

francislavoie commented 10 months ago

@Forsakenrox it was shown off during the Laracon EU talk this morning. https://twitter.com/djgeisi/status/1754791665515049156

I assume they're doing what I wrote above, i.e. using the ReactPHP Redis client to push jobs on the queue to be handled by Horizon or whatever.

palkan commented 10 months ago

Hey folks,

I've been watching this thread for a while (since AnyCable was mentioned here, which I'm the author of), and finally would like to jump into discussion.

@Eyad-Mohammed-Osama The idea you described above ("a Node.JS server that proxies the incoming requests to the Laravel backend through some IPC mechanism") is exactly what you can accomplish with AnyCable without needing to re-invent the WS part or duplicate logic (authN/Z) across services.

We recently added HTTP RPC support (in addition to gRPC one we had for years), so it's just a matter of implementing an HTTP API to process WS commands. Yes, it's HTTP, and performance might be a concern, but: 1) server-to-server HTTP with keepalive is fast enough; 2) you can always switch to more performant gRPC if you hit the limit.

The idea I had in mind for Laravel is to make AnyCable compatible with Echo (like in this example), so you don't need to update the existing code. Another idea is to port Rails Action Cable channels (as an abstraction) to PHP, since it naturally supports bi-directional communication.

Anyway, both require deeper knowledge of Laravel than I have (I have zero 😁), so it only possible if someone from the community would like to collaborate on anycable-laravel.

P.S. We recently released our server(-less) JavaScript SDK serving similar purpose. You can see the amount of gluing code required to seamlessly integrate AnyCable into Laravel projects.

P.S.S. I'm also excited about Reverb and curious how they've implement client-to-server commands (I guess, either HTTP/AJAX emulation or similar HTTP RPC as we have) and what are the delivery guarantees (given Pusher protocol, probably, at-most-once).

Eyad-Bereh commented 10 months ago

@palkan Thanks for mentioning AnyCable, this is the first time I've heard about it

If I understand correctly, you're suggesting a communication mechanism between the Ruby server and Laravel In this case, Laravel would need an anycable broadcasting driver, which is something doable of course. I want to take the initiative here and write this driver, but I just have to wrap my head around AnyCable and the protocol it uses, since I've never used it before.

The option of making AnyCable compatible with Echo can also be done, but it will need that anycable-client be encapsulated for public, private, and presence channels inside Echo by extending the Channel class provided by the client, alongside a connector that extends the Connector class. It's also definitely doable, but I also need to wrap my head around AnyCable in depth, and then this should be fairly simple.