Tarkeshwar Singh
Tarkeshwar Singh

Reputation: 3

NodeJS: both request close and end events not getting fired on browser tab/window close

I am implementing a real time commenting tool using Redis PubSub and NodeJS. On the server, I watch for req close or end event to ensure I close/end the corresponding subscribers. On my OSX dev machine, I am able to detect the close event but on AWS elasticbeanstalk setup, the close event just doesn't get fired. I have tried listening to the other events as well(both for req and res) but those don't seem to be firing either!.

Both my dev and aws setups use Node v0.12.2, Express v4.5.1 nginx 1.6.2 on aws. The aws setup runs behind a load balancer.

Has anyone else ever faced anything similar to this issue?

var publisherClient = redis.createClient();
publisherClient.on("error",function(err){
     console.log(err);
})

setInterval(function(){
     publisherClient.publish("Ping", '{"Pong":'+"}");
},20000)

exports.amastream=function (req,res){
    req.socket.setTimeout(40*1000);
    var messageCount = 0;
    var subscriberClient = redis.createClient();
    subscriberClient.subscribe("Ping");
    subscriberClient.on("error", function(err) {
        console.log("Redis Error: " + err);
    });
    subscriberClient.on("message", function(channel, message) {
        messageCount++;
        res.write('id: ' + messageCount + '\n');
        res.write("data: " +channel + '\n');
        res.write("data: " +message + '\n\n');
        res.flush();
    });
    req.on("end", function() {
        console.log("closing connection due to end of request!");
        subscriberClient.punsubscribe();
        subscriberClient.quit();
    });
    req.on("close", function() {
        console.log("closing connection!");
        subscriberClient.punsubscribe();
        subscriberClient.quit();
    });
    req.on("error", function() {
        console.log("closing connection due to abrupt ending!");
        subscriberClient.punsubscribe();
        subscriberClient.quit();
      });
    req.on("timeout",function() {
        console.log("closing connection due to timeout!");
        subscriberClient.punsubscribe();
        subscriberClient.quit();
    })

    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Connection': 'keep-alive',
        'Cache-Control': 'no-cache',
        'You-Are-Like-Us':'Contact us at [email protected]'
    });
    res.write('id: ' + 0 + '\n');
    res.write("data: " +"first message" + '\n\n');
    res.flush();
}

Upvotes: 0

Views: 1239

Answers (1)

santriseus
santriseus

Reputation: 126

I have the same issue. This behaviour is caused by the Elastic Load Balancing.

We should take a look to the Elastic Load Balancing Connection Timeout Management for better unsderstanind on how ELB works.

So, it has the client and the backend connection and idle timeout value. For some reason ELB can not properly detect when your client connection is closed and close the associated backend connection. Acctually your backend connection stay opened. Thats why you don`t receive the close event.

Idle timeout at the backend side will not work since you are sending ping to client. Howhever, I wonder, where does the ELB send this data if the client connection does not exist?

There is a post on the AWS support forum about this issue. The topic starter tells that it takes approximately 55 mintues to close the connection to backend. From my investigation it could take much longer.

We also use the Server-Sent Events in our application and we discovered that it leads to the enourmous number of idle connections, which is holded by the ELB after the user closed or reloaded the page. You could monitor the number of connections using the following code (I didn`t find a metric at AWS wich shows the number of active 'backend' connection to a particular server from ELB):

    var server = require('http').createServer(app);

    setInterval(function (){
      server.getConnections(function (err, count){
        console.log("Number of connections : " + count);
      });
    }, 60000);

Unfortunately we cant move to the web sockets because EBS does not support it without additional workarounds or using the HAProxy server.

As a workaround it is possible setup a timer wich will perform the req.end each N minutes. In this case client code should detect that server connection is closed and establish a new one.

Upvotes: 2

Related Questions