并发请求到node.js,connect-mongo,会话被覆盖
在当前的一个项目(一种商店系统)中,我使用了expressJS和connect-mongo作为会话存储的node.js。 在客户端,我在启动时使用单个请求来创build一个新的会话,然后将多个并行请求发送到node.js服务器。 由于这些并行请求会改变会话,所以这些改变似乎会相互覆盖,当然这些改变会改变会话的不同对象。
示例(全部3个请求同时开始):
- 请求A将一些产品推送到数组
req.session.productHist['abc']
- 请求B将产品推送到
req.session.productHist['def']
- 请求C需要一些时间,但不会改变会话
由于请求C在请求A和B之后完成,但是在完成之前开始,所以似乎用请求C开始时所保持的值覆盖session.productHist
(null)。
我怎样才能解决这个问题?
更新:
控制台输出的一些示例代码:
var url = require('url'), express = require('express'), MongoStore = require('connect-mongo'); var aDay = 24*60*60*1000; var app = express.createServer(); app.configure(function(){ app.use(express.cookieParser()); app.use(express.session({ secret: "secret", store: new MongoStore({ db: 'lmsCache' }), maxAge: aDay }) ); app.use(express.methodOverride()); app.use(express.bodyParser()); app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); app.use(app.router); app.use(express.logger()); }); function sendStringified(req, res, data) { data = JSON.stringify(data); if (req.url_query.callback) { data = req.url_query.callback + "(" + data + ");"; } res.send(data); } function parseParams(req,res,next) { req.url_query = url.parse(req.url,true).query; next(); } function doExpensiveStuff(req,res,next) { console.log("######################### init start"); [...] } app.get('/init', parseParams, doExpensiveStuff, function(req,res) { console.log("init: session.productHist: " + JSON.stringify(req.session.productHist)); console.log("######################### init end"); sendStringified(req,res,null); }); app.get('/products', parseParams, function(req,res) { console.log("######################### products "+req.url_query.category+" start"); if(!req.session.productHist[req.url_query.category]) req.session.productHist[req.url_query.category] = []; for(var i=0;i<2;i++) { req.session.productHist[req.url_query.category].push({ "id": new Date().toGMTString() }); } console.log("products: session.productHist: " + JSON.stringify(req.session.productHist)); console.log("######################### products "+req.url_query.category+" end"); sendStringified(req,res,[]); }); app.get('/newSession', parseParams, function(req,res) { console.log("######################### newSession"); req.session.productHist = {}; sendStringified(req,res,true); }); app.listen(8080); time = new Date().toGMTString(); console.log('Server starting at: ' + time);
控制台日志:
服务器开始于:星期四,15十二月2011 15:50:37 GMT
################### newSession
################### init start
###################产品-1开始产品:session.productHist:{“-1”:[{“id”:“Thu,15 Dec 2011 15:50:40 GMT”},{“id”:“Thu,15 Dec 2011 15:50:40 GMT “}]}
###################产品-1结束
init:session.productHist:{}
################### init结束
[…]
###################产品-1开始
产品:session.productHist:{“-1”:[{“id”:“Thu,15 Dec 2011 15:50:53 GMT”},{“id”:“Thu,15 Dec 2011 15:50:53 GMT “}]}
###################产品-1结束
我想我已经find了这个棘手问题的答案。
从Express.js文档:
Properties on req.session are automatically saved on a response
简洁版本
当你设置一个会话variables( req.session.my_var = value
)时,实际上并没有保存(在那个时刻),但是后来(当你发送响应的时候,在你的情况下是res.send
)。 这导致了你的问题。
长版
那么究竟是什么意思?
- 你提出一个请求,然后得到会话variables(这是在状态A),然后做一些事情(这需要一些时间)
- 你做了另一个请求,并获得会话variables(仍然处于状态A,因为响应尚未发送),并改变一些东西
- 现在会话处理等已经完成,所以你发送1)的响应,从而修改会话variables,并将其带入状态B.
- 在你发送了2)的回应之后,“快乐”的部分就来了。 现在你实际上并没有修改当前会话(已经更新到状态B),因为响应被延迟了,所以你实际上正在改变的是来自状态A的会话,使它进入状态C =>这意味着所有状态B的修改丢失了!