node.js + socket.io: разработка сайта аукциона
В настоящее время я работаю над сценарием аукциона с использованием node.js и socket.io. Но сайт был разработан с использованием PHP и MySQL. Здесь я использую node.js + socket.io только для аукциона. На сайте будет 500-1000 зарегистрированных пользователей, просматривающих одну страницу во время аукциона. На аукцион будет выставлен только один предмет, и он будет продан за один день.
Я буду транслировать (излучать) таймер обратного отсчета всем пользователям от сервера до клиента. На стороне сервера я буду использовать setInterval(), рекурсивный setTimeout(), равный 1 секунде, для обратного отсчета времени до окончания аукциона. Кроме этого, единственным другим сообщением, которое будет отправлено, будет текущая ставка, передаваемая от одного клиента серверу, а затем рассылаемая всем. Этот способ сделать надежным? И сможет ли он справиться с использованием на сервере? Здесь я протестировал с 500 пользователями средства в браузерах зависания таймера.
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);
});
});
Я жду ваших ответов, чтобы решить проблему зависания браузера.
1 ответ
Первый вопрос: Является ли этот способ надежным?
Отправлять время каждую секунду КАЖДОМУ клиенту не нужно. Просто отправьте им время при первом посещении и используйте локальный таймер (на их локальной странице), чтобы сократить время каждую секунду.
Вам также нужно проверять серверное время на каждую ставку (более безопасную).
Если этого недостаточно для вас, отправьте время с изменяющейся ставкой. Вам нужно только отправить фактическое предложение, когда оно изменилось (с помощью Broadcast) или когда пользователь присоединяется к сайту (просто отправьте его ему).
Второй вопрос: и сможет ли он справиться с использованием на сервере?
И да и нет.
Если ваш сервер достаточно хорош (подойдет каждый 5-долларовый сервер с бесконечным трафиком), у вас не должно быть проблем. Только если ваш сценарий очень-очень плох и засеян утечками памяти.
Теперь несколько советов:
- Никогда не доверяйте пользовательскому вводу - проанализируйте его перед использованием!
- Пересчитайте все, что вы получаете от клиента на сервере.
- Отправляйте Клиенту только то, что ему нужно. Ему не нужна информация о вещах, которые он не использует.
Если это был ответ, на который вы надеялись, выберите зеленую стрелку слева. Если нет, напишите здесь комментарий, и я дам больше советов.