2011-12-14 2 views
5

В текущем проекте (вид системы магазина), я использую Node.js с expressJS и соединить-Монго в качестве сессионного магазина. На стороне клиента я использую один запрос при запуске для создания нового сеанса и отправки нескольких параллельных запросов на сервер node.js. Поскольку эти параллельные запросы меняют сеанс, эти изменения, похоже, переписывают друг друга, хотя, конечно, они меняют разные объекты сеанса.параллельные запросы к Node.js, подключение-Mongo, сессия перезаписаны

Пример (все 3 запроса начинаются в то же время):

  • Запрос толкает некоторые продукты в массив req.session.productHist['abc']
  • Заявка В выталкивает продукты к req.session.productHist['def']
  • Request C занимает некоторое время, но не меняет сеанса

Поскольку запрос C заканчивается после запроса A и B, но начинается до их завершения, он, кажется, перезаписывает session.productHist со значением, которое оно удерживало при запуске запроса C (null).

Как это исправить?

Update:

Некоторые примеры кода с выходом консоли:

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 начать
################### продукты -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 end


[...]

################### продуктов -1 начать

продуктов: session.productHist: { "-1": [{ «id»: «Thu, 15 Dec 2011 15:50:53 GMT»}, {«id»: «Thu, 15 Dec 2011 15:50:53 GMT»}]}

####### ############ products -1 end
+0

Невозможно запросить C после A & B? – alessioalex

+0

Я хотел бы иметь возможность отправлять параллельные запросы, если это возможно. я мог бы отправить A & B после завершения C, но это замедлит работу пользователя. – jb90

+0

Что делает запрос C в любом случае ..? – alessioalex

ответ

3

Я думаю, что нашел ответ на эту сложную проблему.

Из Express.js документации:

Properties on req.session are automatically saved on a response

Короткая версия

Когда вы установите переменную сеанса (req.session.my_var = value), это на самом деле не сохраненную тогда (в тот самый момент), но позже (когда вы отправляете ответ, в вашем случае это когда вы делаете res.send). Это вызвало вашу проблему.

Длинная версия

Так что же это значит, что именно?

  1. Вы делаете запрос, а затем получить переменную сеанса (который находится в состоянии А) и после этого сделать что-то с ним (что требует некоторого времени)
  2. Вы делаете еще один запрос и получить переменную сессии (которая все еще находится в состоянии А, потому что ответ еще не отправлен) и изменить некоторые вещи к нему
  3. Теперь обработка сеанса и т. д. была выполнена, поэтому вы отправляете ответ от 1), тем самым изменяя переменную сеанса и внося ее в состояние B
  4. Здесь идет «забавная» часть, после того как вы отправили ответ от 2). Теперь вы фактически не изменяете текущий сеанс (который был обновлен в состоянии B), потому что ответ был отложен, поэтому то, что вы фактически меняете, это сеанс из состояния A, приводящий его в состояние C =>, что означает все изменения из состояния B были потеряны!
+0

Я думаю, что это проблема, но у вас есть решение для этого? Думаю, я мог бы отразить весь сеанс в памяти node.js, но я ничего не думаю о нем как о хорошем решении, так как это сделает ненужным хранилище mongo до определенной точки (и создаст большие накладные расходы). Я задавался вопросом, есть ли какое-то готовое к использованию решение, потому что я, конечно, не первый, кто спотыкается об этом. – jb90

+1

Не обязательно идеальное решение, но лучше выполнить обработку, а затем вызвать 'req.sessionStore.reload (callback_function)' и в функции обратного вызова изменить сеанс и сразу вызвать 'res.send'. Лучшим решением было бы иметь канал связи (UNIX-сокеты, Redis) и отправлять уведомление при изменении сеанса (таким образом, имитируя блокировку, например, msg: «session: id locked»), и когда вы отправляете ответ для запуска другого сообщения («session: id unlocked»). Каждый req должен прослушиваться, а не изменять сеанс, если он заблокирован (в «разблокированном» сеансе перезагрузки). – alessioalex

+0

Спасибо за вашу помощь :) Я попробую, пока не найду лучшее решение (если найду его). – jb90

Смежные вопросы