Node.js Node/Express:使用会话存储状态时的并发问题
所以,我搜索了很多,发现了几个类似的问题,没有一个真正解决了这个问题,所以我认为这应该是一个自己的问题 我有一个express应用程序,它有一系列路由,可以修改会话以保持状态。问题是,如果有多个并行请求,由于请求之间的竞争条件,会话将不时被覆盖 典型地Node.js Node/Express:使用会话存储状态时的并发问题,node.js,session,express,concurrency,Node.js,Session,Express,Concurrency,所以,我搜索了很多,发现了几个类似的问题,没有一个真正解决了这个问题,所以我认为这应该是一个自己的问题 我有一个express应用程序,它有一系列路由,可以修改会话以保持状态。问题是,如果有多个并行请求,由于请求之间的竞争条件,会话将不时被覆盖 典型地 ... app.use(express.static('/public')); app.use(session(...)); app.route('methodA').get(function(req, res, next) { doSo
...
app.use(express.static('/public'));
app.use(session(...));
app.route('methodA').get(function(req, res, next) {
doSomethingSlow().then(function() {
req.session.a = 'foo';
res.send(...);
}
});
app.route('methodB').get(function(req, res, next) {
doSomethingElseSlow().then(function() {
req.session.b = 'bar';
res.send(...);
}
});
基本上,问题很简单,如答案中所述。Express将会话存储在res.end()中,但在处理methodA请求时,methodB请求可能同时修改了会话,以便methodA存储会话时覆盖methodB所做的任何更改。因此,即使节点是单线程的,并且所有请求都由同一个线程提供服务,只要任何方法执行异步操作,最终都会出现并发问题,从而允许同时处理其他请求
然而,我正在努力决定如何着手解决这个问题。我找到的所有答案都只列出了最小化这种情况发生概率的方法,例如,通过在会话MW之前注册服务静态MW,确保静态内容不会存储会话。但这只是在某种程度上有所帮助;如果事实上存在应该并行调用的API方法,那么就需要一些真正的并发会话更新方法(IMO,当涉及到并发问题时,每一个努力最小化问题发生概率而不是解决实际问题的“解决方案”都注定会出问题)
基本上,这些是我目前正在探索的备选方案:
- 请求A将重新加载会话
- 请求A修改会话。A='foo'
- 请求B重新加载会话(不会看到会话A)
- 请求A存储会话
- 请求B修改会话。B='bar'
- 请求B存储会话,覆盖以前的存储,因此会话.A丢失
谢谢 一个可能的解决方案是为express创建一个简单的中间件,该中间件将维护所有当前请求的列表,并对来自同一会话的任何请求排队。它不会调用next(),直到之前对该会话id的请求首先得到处理 每次请求传入时,它都可以将其存储在对象中,其中键是会话id cookie名称,值是会话id的当前请求数组
{
“会话id1”:[…排队请求…],
“会话id2”:。。。
}
当一个请求完成时,它将从数组中删除它的self,并触发要处理的数组中的下一个请求
您还可以向标头中的每个请求添加一个标志,以允许并发运行不需要排队的请求(例如,它们不写入会话),从而在不需要排队时提高性能
您应该能够实现这一点,而无需更改应用程序。您还可以将标题中的opt-out选项更改为opt-in选项,这意味着它将像通常一样并发处理所有请求,除非另有规定。我们最近遇到了相同的问题。似乎这就是野兽的本性,除了编写我们自己的会话管理器或切换到Redis之外的其他东西,我们没有想出任何更好的选择。我们希望从一开始就做了3件事,但现在改做太贵了。我们的最终解决方案是减少使用会话的请求数(我们使用的是Sails,它会对每个请求执行get/set会话)。我们有一个对象,所有请求在进入节点之前都经过过滤,因此我们将其修改为请求队列(您的选项#1),效果很好。从技术上讲,使用事务更新()使用Redis可以做到这一点,但您必须自己实现,因为connect Redis不支持它。它还可能需要在express会话中进行更改。乐观锁定(在Redis中实现)的问题是,如果更新因并发更新而失败,则必须重新加载新状态,重播更改并再次写入。您可以使用req.session.save()立即保存会话,而不是等待res.end完成it@Omiron那会有什么帮助?仍然没有原子性阻止两个客户端同时执行load modify save