Я использую setTimeout
в Node.js, и, похоже, он ведет себя иначе, чем на стороне клиента setTimeout
, поскольку возвращает объект вместо числа. Я хочу сохранить это в redis, но поскольку redis хранит только строки, мне нужно преобразовать объект в строку. Однако использование JSON.stringify
вызывает ошибку циклической ссылки. Как я могу сохранить этот объект в Redis, если я хочу получить его из Redis и вызвать clearTimeout
?
Сохранение возвращаемого значения setTimeout node.js в redis
Ответы (4)
Вы не можете сохранить объект в Redis. Метод setTimeout
возвращает обработчик (ссылку на объект).
Одна из идей - создать собственный ассоциативный массив в памяти и сохранить индекс в Redis. Например:
var nextTimerIndex = 0;
var timerMap = {};
var timer = setTimeout(function(timerIndex) {
console.log('Ding!');
// Free timer reference!
delete timerMap[timerIndex];
}, 5 * 1000, nextTimerIndex);
// Store index in Redis...
// Then, store the timer object for later reference
timerMap[nextTimerIndex++] = timer;
// ...
// To clear the timeout
clearTimeout(timerMap[myTimerIndex]);
clearTimeout
для объекта после его получения из redis
- person user730569; 03.07.2012
Я пытался сделать то же самое, что и OP. Мое решение заключалось в том, чтобы установить тайм-аут с условной проверкой нового ключа внутри тайм-аута в моем обработчике отключения:
redis.hset("userDisconnecting:" + userId, "disconnect", 1);
setTimeout(function() {
redis.hget("userDisconnecting:" + userId, "disconnect",
function(err, result) {
if (result.toString() === "1") {
//do stuff, like notify other clients of the disconnect.
}
});
}, 10000);
Затем, когда клиент снова подключается, я устанавливаю этот ключ на 0
, чтобы то, что должно срабатывать при истинном отключении, не происходило:
redis.hset("userDisconnecting:" + userId, "disconnect", 0);
Сами таймауты не сохраняются при перезапуске сервера, но вы можете решить эту проблему, запустив метод очистки при запуске. Подключенные клиенты довольно быстро возвращались в «онлайн».
Этот код используется, когда тайм-ауты не должны сохраняться при перезапусках сервера.
var timeouts = {};
app.get('/', function (req, res) {
var index = timeouts.length;
timeouts[index] = setTimeout(console.log, 1000000, req.user.name);
redis.set('timeout:' + req.user.name, index, function (err, reply) {
res.end();
});
});
app.get('/clear', function (req, res) {
redis.get('timeout:' + req.user.name, function (err, index) {
clearTimeout(timeouts[index]);
delete timeouts[index];
redis.delete('timeout:' + req.user.name);
res.end();
});
});
Если вам нужно, чтобы тайм-ауты сохранялись при перезапусках сервера, вам может потребоваться сохранить значения _idleStart
и _idleTimeout
для каждого таймера в redis и загружать их каждый раз при перезапуске сервера.
app.get('/', function (req, res) {
var timeout = setTimeout(console.log, 1000000, req.user.name);
var time = timeout._idleStart.getTime() + timeout._idleTimeout;
redis.set('timeout:' + req.user.name, time, function (err, reply) {
res.end();
});
});
app.get('/clear', function (req, res) {
redis.delete('timeout:' + req.user.name);
res.end();
});
// Load timeouts on server start
// *I know this is not the correct redis command*
// *It's not accurate, only approx*
redis.get('timeout:*', function (err, vals) {
vals.forEach(function (val) {
var time = val - new Date().getTime();
setTimeout(console.log, time, username)
});
});
В более новых версиях узла вы можете использовать идентификатор объекта Timeout
вместо самого объекта для завершения цикла.
redisClient.set('time', JSON.stringify(10))
let timeoutObject = setInterval(async function(){
let time = await JSON.parse(redisClient.get('time'))
if(time === 0){
let intervalId = await JSON.parse(redisClient.get('intervalId'))
clearInterval(intervalId)
}
time -= 1
redisClient.set('time', JSON.stringify(time))
}, 1000)
let intervalId = timeoutObject[Symbol.toPrimitive]()
redisClient.set('intervalId', JSON.stringify(intervalId))
Это всего лишь пример таймера, построенного с комбинированием setInterval
и redis
. Как видите, вы можете получить идентификатор объекта тайм-аута и сохранить его, чтобы завершить выполнение setInterval, вместо того, чтобы пытаться сохранить весь объект.
Вот ссылка на документы узла: https://nodejs.org/api/timers.html#timers_timeout_symbol_toprimitive
setTimeout
имеет какое-либо отношение к ошибке циклической ссылки. stackoverflow.com/questions/1493453 / - person Trevor   schedule 03.07.2012setTimeout
создает этот объект:{ _idleTimeout: 1000000000, _idlePrev: { _idleNext: [Circular], _idlePrev: [Circular], ontimeout: [Function] }, _idleNext: { _idleNext: [Circular], _idlePrev: [Circular], ontimeout: [Function] }, _onTimeout: [Function], _idleStart: Mon, 02 Jul 2012 20:28:18 GMT }
- person user730569   schedule 03.07.2012_idleNext
и_idlePrev
кажутся циклическими ссылками ... - person user730569   schedule 03.07.2012RedisStore
socket.io - см., Например, этот мой ответ. - person Linus Thiel   schedule 03.07.2012RedisStore
Socket.io. Проблема заключается в том, чтобы сохранить возвращаемое значение методаsetTimeout
. - person user730569   schedule 03.07.2012disconnect
, но если они возвращаются в течение X секунд, я беру таймер из хранилища и очищаю его. В противном случае таймер выдает событие случайного отключения по истечении этих X секунд. - person user730569   schedule 03.07.2012emit
подключиться к каналу на том экземпляре, который изначально обрабатывал пользователя. Я почти уверен, что RedisStore обработает распространение на другие экземпляры. - person Linus Thiel   schedule 03.07.2012publish/subscribe
? - person user730569   schedule 03.07.2012MemoryStore
, если вы это сделаете, напримерio.sockets.of('foo').emit('foo', 'bar')
, который будет отправлен всем клиентам, подключенным к этому экземпляру. При использованииRedisStore
сообщение будет отправлено всем клиентам во всех экземплярах с использованиемpublish/subscribe
материала redis. - person Linus Thiel   schedule 03.07.2012socket.join("channel name");
- person user730569   schedule 03.07.2012var socket = io.connect(); socket.join('foo');
присоединится к комнате'foo'
, аvar socket = io.connect('/foo')
также присоединится к комнате'foo'
. - person Linus Thiel   schedule 03.07.2012setTimeout
, в redis, как я смогу захватить этот объект и сбросить тайм-аут при повторном подключении клиента? Есть ли способ убедиться, что клиент повторно подключается к тому же серверу в том же экземпляре? В противном случае вновь подключенный сервер не сможет найти объект таймера на предыдущем сервере, если он находится в памяти с другим экземпляром и / или другим процессом узла, верно? - person user730569   schedule 28.11.2012