Spring Session管理可以处理异步调用吗?
假设我们有多个控制器,每个控制器都在读/写不同的会话属性。会话对象是完全从外部服务器写入/从外部服务器读取的,而不是仅从属性读取的,是否会存在并发问题?
我们面临这样一个问题,即控制器的属性集在下一次读取中不存在...这是一个间歇性问题,具体取决于并行执行其他控制器。
当我们使用容器中的会话对象时,我们从未遇到过这个问题……假设它是直接属性设置/获取,正好发生在内存中的会话对象上。
答案 0 :(得分:1)
会话的一般用例是存储一些用户特定的数据。如果我正确地理解了您的上下文,则您的问题描述的是这样一种情况:某个用户(例如,从两个设备(例如,PC和电话)进行身份验证,因此处于同一会话范围内)正在向后端发送请求这么快,您将面临与读写会话数据有关的并发问题。
对于该会话来说,这不是常见的情况(恕我直言,也是合理的),因此spring-data-redis
或spring-data-gemfire
之类的项目将无法立即使用。
好消息是,春季会议在构建时就考虑了灵活性,因此您当然可以实现所需的目标。您可以实现自己的SessionRepository
版本,并手动同步(例如,通过Redis分布式锁)相关方法。但是,在执行此操作之前,请检查您的设计并确保将会话用于正确的数据存储作业。
答案 1 :(得分:1)
此问题与您的last question本质上非常相似。并且,在阅读我的答复/评论之前,您应该先阅读my answer,然后再回答。
匿名用户发布的先前答案(和见解)相当准确。
每当您有一个高度并发的(Web)应用程序/环境时,会有许多不同的并发HTTP请求进入,访问同一HTTP会话,则总是有 丢失更新的可能性是由竞争并发HTTP请求之间的 竞争条件 引起的。这是由于Servlet容器(例如Apache Tomcat或Eclipse Jetty)的本质,因为每个HTTP请求均由单独的线程处理,并在单独的线程中处理。
Servlet容器提供的HTTP会话对象不仅需要是线程安全的,而且Web应用程序放入HTTP会话中的所有应用程序域对象也必须是线程安全的。因此,请注意这一点。
此外,大多数HTTP会话实现(例如Apache Tomcat的,甚至是 Spring会话的)由不同会话管理提供程序支持的会话实现(例如, Spring会话数据Redis ,或 Spring会话数据GemFire )广泛使用“增量”,仅将更改(或差异)发送到Session状态,从而最大程度地减少了由于以下原因导致的丢失更新的可能性: 比赛条件。
例如,如果HTTP会话当前具有属性键/值1/A
,并且HTTP请求1(由线程1处理)将读取HTTP会话(仅包含1/A
)并添加属性2/B
,而另一个并发HTTP请求2(由线程2处理)通过会话ID(与1/A
看到相同的初始会话状态)读取相同的HTTP会话,现在想添加{{1 }},然后作为Web应用程序开发人员,我们期望在线程1和2中的请求1和2完成之后,最终结果和HTTP会话状态将包含属性3/C
。
但是,如果2个(或什至更多)竞争的HTTP请求都在修改,请说HTTP sessoin属性[1/A, 2/B, 3/C]
,而HTTP请求/线程1希望将属性设置为1/A
,并且竞争的HTTP请求/线程2希望将相同的属性设置为1/B
,然后谁赢了?
事实证明,最后1个获胜,或更确切地说,写入HTTP会话状态的最后一个线程获胜,结果可能是1/C
或1/B
,这是不确定的,并受制于调度,网络延迟,负载等方面的变化无常。实际上,几乎不可能推断出哪一个会发生,而很少会总是发生。
虽然我们的匿名用户与同时使用多个设备(例如Web浏览器,也许是移动设备...智能手机或平板电脑)的用户提供了某种上下文,但单个用户甚至多个用户都会重现这种错误。用户并非不可能,但非常不可能。
但是,如果我们在生产环境中考虑这一点,则可能有数百个Web应用程序实例分布在多个物理机器,VM,容器等上,并由某些网络负载均衡器/设备,然后发现当今许多Web应用程序都是“单页应用程序”,高度复杂的非哑(不再瘦)但具有JavaScript和AJAX调用的胖客户端这一事实,那么我们开始了解到这种情况远不止于此可能,尤其是在高负载的Web应用程序中;想想亚马逊或Facebook。给定Web应用程序可以进行的所有动态,异步调用,不仅有许多并发用户,而且单个用户有许多并发请求。
不过,正如我们的匿名用户指出的那样,这并不能为Web应用程序开发人员负责任地设计和编码Web应用程序辩解。
通常,我会说HTTP会话仅应用于跟踪非常少(即数量)和必要的信息,以保持良好的用户体验并在用户过渡时保持用户与应用程序之间的正确交互。 Web应用程序的不同部分或阶段,例如(在购物车中)跟踪首选项或项目。通常,不应将HTTP会话用于存储“交易”数据。这样做是要让自己陷入麻烦。 HTTP会话主要应该是读取繁重的数据结构(而不是写入繁重的数据),尤其是因为HTTP会话可以并且很可能将从多个线程访问。
当然,不同的后备数据存储区(例如Redis,甚至GemFire)都提供锁定机制。 GemFire甚至提供了高速缓存级别的事务,这在处理HTTP会话对象中以及由HTTP会话对象管理的Web交互时非常繁琐且可争议(不与事务混淆)。甚至锁定也会给应用程序带来严重的争用和延迟。
无论如何,所有这一切都意味着您非常需要了解交互和数据访问模式,否则您会陷入困境,因此请始终保持谨慎!
值得深思!