Node / Express:使用会话存储状态时的并发问题

所以我search了一下,发现了几个类似的问题,但都没有真正解决这个问题,所以我觉得这个问题本身就是值得的。

我有一个修改会话保持状态的一堆路由的快速应用程序。 事情是,如果有多个并行请求,由于请求之间的竞争条件,会话会不时被覆盖。

所以通常

... 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所做的任何更改。 因此,即使节点是单线程的,并且所有请求都由同一个线程提供服务,只要有任何方法正在执行某种asynchronous操作,我们就可以最终解决并发问题,从而让其他请求同时处理。

但是,我正在努力决定如何着手解决这个问题。 所有我find的答案只列出了最小化发生这种情况的可能性的方法,例如通过确保静态内容不通过在会话MW之前注册服务静态MW来存储会话。 但这只是在一定程度上有所帮助; 如果实际上API方法应该被并行调用,那么需要一些实际并发的会话更新方法(IMO,当涉及到并发问题时,每个“解决scheme”都力求最小化发生问题的可能性而不是寻址实际问题肯定会出错)。

基本上这些是我目前正在探索的select:

  1. 完全通过修改我的客户端来防止在同一个会话中的并行请求,以确保它连续调用所有的API方法。

    这可能是可能的,但会对我的架构产生相当大的影响,并可能影响性能。 它也避免了这个问题,而不是解决这个问题,如果我的客户端出了问题,或者另一个客户端使用了API,我可能还会随机遇到这个问题,所以它不会很健壮。

  2. 确保每个会话写入之前都有一个会话重新加载,并使整个重新加载 – 修改 – 写入操作成为primefaces。

    我不知道如何做到这一点。 即使在修改和存储会话之前修改res.end()来重新加载会话,因为读取和写入会话是asynchronousI / O,似乎可能会发生这种情况:

    • 请求A重新加载会话
    • 请求A修改session.A ='foo'
    • 请求B重新加载会话(并不会看到session.A)
    • 请求A存储会话
    • 请求B修改session.B ='bar'
    • 请求B存储会话,覆盖以前的商店,以便session.A丢失

所以实质上,我需要使每个reload-modify-storeprimefaces,这基本上意味着阻塞线程? 感觉错了,我不知道该怎么做。

  1. 以这种方式完全停止使用会话,并将必要的状态作为parameter passing给每个请求或通过其他方式。

    这也避免了这个问题,而不是解决它。 这也会对我的项目产生巨大的影响。 但是,当然,这可能是最好的方法。

  2. ???

任何人有任何想法如何以强大的方式解决这个问题? 谢谢!

一个可能的解决scheme是创build一个简单的中间件express,它将维护当前所有请求的列表,并排队来自同一个会话的任何请求。 它将不会调用next(),直到先前处理该session-id的请求为止。

每次请求进来时,都可以将它存储在一个对象中,其中的关键字是session-id cookie名称,值是session-id当前请求的数组。

{ 'session-id1': [... queued requests ...], 'session-id2': ... }

当一个请求完成时,它将从数组中移除它自己,并触发数组中的下一个请求进行处理。

您还可以为标头中的每个请求添加一个标志,以允许并发运行不需要排队的请求(例如,它们不写入会话),从而在不需要排队时提高性能。

你应该能够实现这个,而不必改变你的应用程序。 您也可以将标题中的退出选项更改为selectjoin选项,而不是像通常那样同时处理所有请求,除非另有规定。