Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel Echo repeating listen event for n times broadcast has been made

Question title may be little confusing, I tried my best to explain it.

I've got laravel-echo-server, redis (for queueing), and Laravel echo set up. All I want is to send a message via an input, and update the messages feed for everyone on the page; essentially a chat room.

Everything works, except for the fact it's slow as hell, but I'm new to Laravel Echo and Vue.

if I sent one message, it's fine, but then when I send another message I get two responses. Send a third message three responses and so on...

What exactly am I doing wrong? I have checked socket.io/laravel-echo-server is only connecting to the channel once, and my broadcast is only being sent once (checking queue worker it's only processing the request), so how is it being repeated exponentially?

This is my app.js:

const chatWindow = new Vue({
    el: '#chatWindow',
    data: {
        messages: []
    },
    methods: {
        sendMessage: function () {
            this.$http.post('/chat', {message: this.message}).then((response) => {
                window.Echo.channel('test-chat-channel')
                    .listen('MessageSent', (event) => {
                        console.log(event.message);
                        // this.messages.push(event.message);
                    });
            }, (response) => {
                // error callback
            });

            this.message = '';
        }
    },
    props: ['message']
});

This is my chat view:

<div id="chatWindow" class="col-sm-7">
    <h1 class="text-center no-top">Chat</h1>
    <div class="chat-window">
        <ul>
            <li v-for="message in messages">@{{ message }}</li>
        </ul>

        <div class="form-group">
            <input title="Send Message" v-model="message" @keyup.enter="sendMessage" class="form-control" placeholder="Send message...">
        </div>
    </div>
</div>

Just in case they're relevant here is my route:

Route::post('/chat', function (Request $request) {
    event(new MessageSent($request->message));
});

and my event:

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class MessageSent implements ShouldBroadcast
{
    use InteractsWithSockets, SerializesModels;

    public $message;

    public function __construct($message)
    {
        $this->message = $message;
    }

    public function broadcastOn()
    {
        return new Channel('test-chat-channel');
    }
}
like image 940
A. Appleby Avatar asked Nov 28 '16 23:11

A. Appleby


2 Answers

I am using Redis PubSub in the application I am currently building and have had to fight the same problem. While I don't know laravel-echo-server enough to advise you specifically on how to fix it, I can describe the likely reason for this symptom.

Your configuration is subscribing to the Redis channel on each cycle, and that subscription is not transient. Each time you subscribe, you create a new listener. Each time you publish, Redis sends the message to all listeners/subscribers. If a single client has subscribed three times, it will receive that message three times. You need to either recognize that you are subscribed and not duplicate it, or you need to unsubscribe after each cycle.

One thing to note is that the Redis instance should either publish or subscribe, but not both. laravel-echo-server is probably handling that for you.

Redis normally returns the number of subscribers receiving a message on each publish. This helped me diagnose the problem, especially since I am running private channels and should have 1, and only 1, listener for each message. Redis also has a "numsub" command that returns the number of listeners on a channel.

I noticed that your sendMessage function uses the listen method. It's a shot in the dark, but is that where you get multiple subscriptions?

Either way, I'd be glad to help further if you have a specific question that I can answer.

like image 63
Richard_G Avatar answered Oct 21 '22 12:10

Richard_G


For anyone coming across this who is seeing a similar issue where:

  • You have a single page application
  • You have defined a single Echo listener
  • You have enabled Pusher console logs and see only one event received
  • You see only one event fired in the Pusher debug console
  • You see a normal operations in the debug console (one subscription, disconnection etc. to the correct channel)
  • BUT your listener is receiving multiple events each time a single event is fired

It could be that your Pusher connection is not being destroyed when each page is loaded. This can happen if you have a global component which has been registered on multiple pages. To overcome this issue, you could register your listener component within a master layout component, preventing multiple listeners from being cached.

like image 26
RonnyKnoxville Avatar answered Oct 21 '22 13:10

RonnyKnoxville