好,實現自己的存儲可能是你的選擇。 This documentation表明您需要做的只是實現三種方法:.get
,.set
和.destroy
(請參見最後一段)。這將是這樣的(使用node-redis library和修改the original connect-redis store了一下):
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),但你應該理解這個想法。這裏的the link其中包含關於如何使用setnx
來鎖定/解鎖Redis的討論。
其他注意事項:您可能想爲路由進行一些自定義錯誤處理,因爲如果任何路由拋出異常,則Redis會話將不會被解鎖。 .set
方法始終被稱爲路由中的最後一件事 - 與在路由開始時Express呼叫的.get
方法相反(這就是爲什麼我鎖定在.get
並解鎖於.set
)。儘管如此,你仍然只能鎖定5秒鐘,所以它不一定是個問題。請記住調整它以滿足您的需求(尤其是threshold
和wait_time
變量)。
最後注意事項:有了這個機制,你的請求處理程序將只會爲每個用戶一個接一個地觸發。這意味着,你會不能以爲每個用戶運行併發處理程序。這可能是一個問題,所以另一個想法是在會話之外保存數據並手動處理鎖定/解鎖。畢竟,有些東西需要手動處理。
我希望它有幫助!祝你好運!
嗨!感謝您的非常詳細的解釋!我已經成功地使用2箇中間件鎖定了會話(在創建會話之前和請求完成之後)。我想如果我想要支持用戶會話中的併發性,手動方法將是最好的。 – liorix 2012-07-11 11:35:08