我正在使用具有connect-redis的nodejs来存储会话数据。
我将用户数据保存在会话中,并在会话生存期内使用它。
我注意到,在两个更改会话数据的请求之间可能存在竞争状态。
我尝试使用redis-lock锁定会话,但是对我来说有点麻烦。
我不想锁定整个会话,而是只锁定特定的会话变量。
我发现这是不可能的,我考虑了解决的方向:
停止使用会话对象存储用户数据,并将变量直接保存在redis中并在使用前锁定。
我知道它可以工作,但是需要我手动管理所有对象,而不仅仅是通过会话对象访问redis。
您能否与我分享最佳实践和您的建议?
谢谢,Lior
嗯,实现自己的存储可能是您的选择。该文件显示,所有你需要做的是实现三个方法:.get
,.set
和.destroy
(参见最后一段)。就像这样(使用node-
redis库
并修改原始的connect-
redis存储
有点):
var redis = require("redis"),
redis_client = redis.createClient(),
session_prefix = 'session::',
lock_suffix = '::lock',
threshold = 5000,
wait_time = 250,
oneDay = 86400;
/* If timeout is greater then threshold, then we assume that
one of the Redis Clients is dead and he cannot realese
the lock. */
function CustomSessionStore(opts) {
opts = opts || {};
var self = this;
self.ttl = opts.ttl; // <---- used for setting timeout on session
self.lock = function(sid, callback) {
callback = callback || function(){};
var key = session_prefix + sid + lock_suffix;
// try setting the lock with current Date
redis_client.setnx(key, Date.now( ), function(err, res) {
// some error handling?
if (res) {
// Everything's fine, call callback.
callback();
return;
}
// setnx failed, look at timeout
redis_client.get(key, function(err, res) {
// some error handling?
if (parseInt(res) + threshold > Date.now( )) {
// timeout, release the old lock and lock it
redis_client.getset(key, Date.now( ), function(err, date) {
if (parseInt(date) + threshold > Date.now()) {
// ups, some one else was faster in acquiring lock
setTimeout(function() {
self.lock(sid, callback);
}, wait_time);
return;
}
callback();
});
return;
}
// it is not time yet, wait and try again later
setTimeout(function() {
self.lock(sid, callback);
}, wait_time);
});
});
};
self.unlock = function(sid, callback) {
callback = callback || function(){};
var key = session_prefix + sid + lock_suffix;
redis_client.del(key, function(err) {
// some error handling?
callback();
});
};
self.get = function(sid, callback) {
callback = callback || function(){};
var key = session_prefix + sid;
// lock the session
self.lock(sid, function() {
redis_client.get(key, function(err, data) {
if (err) {
callback(err);
return;
}
try {
callback(null, JSON.parse(data));
} catch(e) {
callback(e);
}
});
});
};
self.set = function(sid, data, callback) {
callback = callback || function(){};
try {
// ttl used for expiration of session
var maxAge = sess.cookie.maxAge
, ttl = self.ttl
, sess = JSON.stringify(sess);
ttl = ttl || ('number' == typeof maxAge
? maxAge / 1000 | 0
: oneDay);
} catch(e) {
callback(e);
return;
}
var key = session_prefix + sid;
redis_client.setex(key, ttl, data, function(err) {
// unlock the session
self.unlock(sid, function(_err) {
callback(err || _err);
});
});
};
self.destroy = function(sid, callback) {
var key = session_prefix + sid;
redis_client.del(key, function(err) {
redis_client.unlock(sid, function(_err) {
callback(err || _err);
});
});
};
}
旁注
:我没有为.lock
和实现错误处理.unlock
。我把这个留给你!:)可能会有一些小错误(目前我没有NodeJS,我正在从我的记忆中写它:D),但是您应该理解这个想法。这是包含有关如何用于锁定/解锁Redis
的讨论的链接setnx
。
另一个注意事项
:您可能想对路由进行一些自定义错误处理,因为如果任何路由引发异常,则Redis会话将不会被解锁。该.set
方法始终被称为路线中的最后一件事-与.get
Express
在路线的最开始处调用的方法相反(这就是为什么我在处锁定.get
并在处解锁.set
)。仍然您只会被锁定5秒钟,因此虽然不必担心。请记住,它调整到您的需求(尤其是threshold
和wait_time
变量)。
最后说明 :使用这种机制,您的请求处理程序将仅对每个用户一个接一个地触发。这意味着您将 无法为
每个用户运行并发处理程序。这可能是一个问题,因此另一个想法是将数据保留在会话外部并手动处理锁定/解锁。毕竟,有些事情必须手动处理。
希望对您有所帮助!祝好运!
问题内容: 我有以下T-SQL代码: 我正在使用带有注释的“锁定”黑客来获取排他锁定。 注意:使用TABLOCKX或UPDLOCK提示将不起作用,因为我通过拆分语句并在中间添加WAITFOR命令进行测试而破坏了ATOMIC- ity。我不要这样的东西: 在同时运行两个会话(带锁表)之后,这是正确的结果 那是运行带有注释的代码的不正确结果 没有这样的黑客,有没有更好的方法来获取交易范围内的排他锁?
我正在做分类帐模块。在这个过程中,我必须按顺序完成这些任务 从源中选择余额(table 1 row1) 从目标中选择余额(table 1 row2) 用一些逻辑修改余额 更新源的余额(table 1 row1) 更新目标余额(table 1 row2) 提交更改 将事务插入到事件表中。 在多线程环境中,线程在前一个线程更新和提交之前获得余额。在Postgres中,锁被强加给正在被访问的行,直到线程
在使用ACK时,有没有一种简单的方法实现类似于“锁定”的东西来防止RabbitMQ队列中的竞争条件? 我有以下问题--我有几个客户机使用ACK使用队列。每当客户端收到消息时,他就会确认并处理消息。但是,如果由于某种原因处理失败,我希望消息返回到队列。
问题内容: 我写了一个函数来为一个简单的博客引擎创建帖子: 当多个用户同时删除标签并创建帖子时,是否容易出现竞争状况? 具体来说,交易(以及功能)是否可以防止此类竞争情况的发生? 我正在使用PostgreSQL 9.2.3。 问题答案: 它的经常性问题 _ 或下可能并发写入负载,涉及(但不同于)(这是 或_)。 对于Postgres 9.5或更高版本 使用新的UPSERT实施,我们可以大大简化。P
问题内容: 如何防止多个客户端使用相同的会话ID?我之所以这样询问,是因为我想增加一层安全保护,以防止网站上的会话劫持。如果黑客以某种方式弄清楚另一个用户的会话ID并使用该SID发出请求,我如何检测到服务器上有不同的客户端共享一个SID,然后拒绝劫持尝试? 编辑 经过深思熟虑后,我接受了Gumbo的回答,因为我意识到由于 无状态HTTP协议 的限制,我所要求的是不可能的。我忘了HTTP的最基本原理
主要内容:锁住共享资源有并发,就有资源竞争,如果两个或者多个 goroutine 在没有相互同步的情况下,访问某个共享的资源,比如同时对该资源进行读写时,就会处于相互竞争的状态,这就是并发中的资源竞争。 并发本身并不复杂,但是因为有了资源竞争的问题,就使得我们开发出好的并发程序变得复杂起来,因为会引起很多莫名其妙的问题。 下面的代码中就会出现竞争状态: 这是一个资源竞争的例子,大家可以将程序多运行几次,会发现结果可能是