Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

node.js + socket.io: auction website development

I am currently working on an auction script using node.js and socket.io.But site was developed by using PHP & MySQL. Here I'm using node.js + socket.io for auction bidding process only. The site will have 500-1000 logged in users viewing a single page during the auction. Only one item will be on auction and it will be sold at one day once.

I will be broadcasting(emitting) a countdown timer to all of the users from the server to the client. On the server side I will be using setInterval(),recursive setTimeout() of 1 second to countdown to the auction end time. Apart from this the only other message being sent across will be the current bid being passed from a single client to the server then broadcast to all. This way to do be a reliable? And will it be able to handle the usage on the server?. Here I've tested with 500 users means in browsers getting hanging the timer.

Server.js

var cluster = require('cluster');
var app = require('express')();
//var http = require('http');
var https = require('https');
var socket = require('socket.io');
var redis = require('redis');
var redisAdapter = require('socket.io-redis');
var request = require('request');
var fs = require('fs');

var options = {
    key: fs.readFileSync('keys/e1317_0f2c9_71565598d419e37e376ccef5c2827113.key'),
    cert: fs.readFileSync('certs/e1317_0f2c9_1468152279_2dc46c1f2cc135a.crt'),
    ca: fs.readFileSync('cabundles/90490a5c829d2aca24f22b5820864c6e_1935558000.cabundle')
};

//var server = http.createServer( app );
var server = https.createServer(options, app);
var io = socket.listen(server);

var port = process.env.PORT || 8080;
var workers = process.env.WORKERS || require('os').cpus().length;

var redisUrl = process.env.REDISTOGO_URL || 'redis://127.0.0.1:6379';
var redisOptions = require('parse-redis-url')(redis).parse(redisUrl);
var pub = redis.createClient(redisOptions.port, redisOptions.host, {
    detect_buffers: true,
    return_buffers: true,
    auth_pass: redisOptions.password
});
var sub = redis.createClient(redisOptions.port, redisOptions.host, {
    detect_buffers: true,
    return_buffers: true,
    auth_pass: redisOptions.password
});

io.adapter(redisAdapter({
    pubClient: pub,
    subClient: sub
}));

console.log('Redis adapter started with url: ' + redisUrl);

io.sockets.on('connection', function(client) {
    //console.log('first');
    client.on('nauction', function(data) {
        io.sockets.emit('nauction', data);
    });
});

io.on('connection', function(socket) {
    //console.log('in');
    console.log('connected client count:' + io.sockets.sockets.length);
    var recursive = function() {
        //console.log("It has been one second!");
        if (io.sockets.sockets.length > 0) {
            request('https://www.example.com/file.php', function(error, response, body) {
                if (!error && response.statusCode == 200) {
                    data = JSON.parse(body);
                    socket.volatile.emit('auction_data', {
                        'auction_data': data
                    });
                    //console.log(data);
                } else {
                    //console.log('else');
                    console.log(error);
                }
            });
        } //else{
        //console.log('No clients connected now');
        //}
        setTimeout(recursive, 1000);
    }
    recursive();
    socket.on("disconnect", function() {
        console.log('clear interval')
            //clearInterval(interval);
        clearTimeout(recursive);
    });
});

if (cluster.isMaster) {
    console.log('start cluster with %s workers', workers - 1);
    workers--;
    for (var i = 0; i < workers; ++i) {
        var worker = cluster.fork();
        console.log('worker %s started.', worker.process.pid);
    }

    cluster.on('death', function(worker) {
        console.log('worker %s died. restart...', worker.process.pid);
    });
} else {
    start();
}

function start() {
    server.listen(port, function() {
        console.log('listening on *:' + port);
    });
}

Client.js

socket.on('auction_data', function(auction_details) {
    //console.log(auction_details);
    $.each(auction_details, function(keys, values) {
        //countdwon formation
        var tm, days, hrs, mins, secs;
        days = value.auction_data.time.days;
        if (value.auction_data.time.hours < 10) {
            hrs = ("0" + value.auction_data.time.hours);
        } else {
            hrs = value.auction_data.time.hours;
        }
        if (value.auction_data.time.mins < 10) {
            mins = ("0" + value.auction_data.time.mins);
        } else {
            mins = value.auction_data.time.mins;
        }
        if (value.auction_data.time.secs < 10) {
            secs = ("0" + value.auction_data.time.secs);
        } else {
            secs = value.auction_data.time.secs;
        }
        if (days == 0) {
            tm = '' + hrs + '' + '' + mins + '' + '' + secs + '';
        } else {
            tm = '' + days + '' + '' + hrs + '' + '' + mins + '' + '' + secs + '';
        }
        $('#auction_' + value.auction_data.product_id + " .countdown").html(tm);
    });
});

I'm waiting for your answers to fix the browser hanging problem.

like image 727
sankar muniyappa Avatar asked Jan 25 '16 12:01

sankar muniyappa


1 Answers

First Question: Is This way to do be a reliable?

Sending the time every Second to EVERY client is not necessary. Simply send them the time at their first visit and use a local timer (at their local page) to reduce the time every second.

You also need to check for server-time on every bid (more secure).

If this is not "secure" enough for you, send the time with the changing bid. You only have to send the actual Bid, when it changed (using Broadcast) or when the user joins the site (just send it to him).

Second Question: And will it be able to handle the usage on the server?

Yes and No.

If your Sever is good enough (every 5$ server with endless traffic would fit), you should not get in trouble. Only, if your script is very very bad and seeded with Memory Leaks.

Now a few tips:

  • Never trust the user input - parse it before you use it!
  • Recalculate everything you get from the client on the Server.
  • Send the Client only what he needs. He does not need information about stuff that he does not use.

If this was the answer you hoped for, please select the green arrow on the left. If not, write a comment here and I will give more tips.

like image 126
Mijago Avatar answered Oct 16 '22 17:10

Mijago