【问题标题】:Spring Session - asynchronous call handlingSpring Session - 异步调用处理
【发布时间】:2018-09-29 04:07:41
【问题描述】:

Spring Session 管理是否负责异步调用?

假设我们有多个控制器,每个控制器都在读/写不同的会话属性。由于会话对象完全写入/读取外部服务器,而不是单独的属性,是否会出现并发问题?

我们面临这样一个问题,即从控制器设置的属性在下一次读取中不存在...这是一个间歇性问题,取决于其他控制器的并行执行。

当我们使用容器中的会话对象时,我们从未遇到过这个问题...假设它是直接发生在内存中的会话对象上的属性设置/获取。

【问题讨论】:

  • 好吧,如果进行了异步调用,那么如果第一个异步请求仍未完成会话创建任务以及在第二个异步请求之间读取该会话的属性会发生什么

标签: spring spring-session spring-data-redis gemfire spring-data-gemfire


【解决方案1】:

会话的一般用例是存储一些用户特定的数据。如果我正确理解您的上下文,则您的问题描述了用户在通过两个设备(例如 PC 和电话 - 因此在同一个会话的范围内)进行身份验证时向您的后端发送请求的场景如此之快,您将面临读写会话数据的并发问题。

这不是会话的常见(恕我直言)场景,因此 spring-data-redisspring-data-gemfire 等项目不会开箱即用地支持它。

好消息是 spring-session 在构建时考虑了灵活性,因此您当然可以实现您想要的。您可以实现自己的SessionRepository 版本并手动同步(例如通过Redis 分布式锁)相关方法。但是,在此之前,请检查您的设计并确保您使用会话进行正确的数据存储工作。

【讨论】:

    【解决方案2】:

    这个问题本质上与您的last question 非常相似。而且,在阅读我的回复/cmets 之前,您应该阅读该问题的 my answer

    匿名用户之前发布的答案(和见解)相当准确。

    只要您有一个高度并发的 (Web) 应用程序/环境,其中有许多不同的同时 HTTP 请求进入,访问同一个 HTTP 会话,总是有可能丢失更新 由竞争的并发 HTTP 请求之间的竞争条件引起。这是由于 Servlet 容器(例如 Apache Tomcat 或 Eclipse Jetty)的本质,因为每个 HTTP 请求都由单独的线程处理并在单独的线程中处理。

    不仅 Servlet 容器提供的 HTTP 会话对象需要是线程安全的,您的 Web 应用程序放入 HTTP 会话的所有应用程序域对象也需要是线程安全的。所以,请注意这一点。

    此外,大多数 HTTP 会话实现,例如 Apache Tomcat,甚至 Spring Session 的 由不同会话管理提供程序(例如 Spring Session Data Redis 支持的会话实现,或Spring Session Data GemFire)广泛使用“deltas”来仅将更改(或差异)发送到 Session 状态,从而最大限度地减少丢失更新的机会竞争条件

    例如,如果 HTTP 会话当前有一个属性键/值 1/A 并且 HTTP 请求 1(由线程 1 处理)读取 HTTP 会话(只有1/A)并添加一个属性2/B,而另一个并发 HTTP 请求 2(由线程 2 处理)通过会话 ID 读取相同的 HTTP 会话(使用1/A 看到相同的初始会话状态),现在想要添加3/C,那么作为 Web 应用程序开发人员,我们在线程 1 和 2 中的请求 1 和 2 完成后,期望最终结果和 HTTP 会话状态包含属性:[1/A, 2/B, 3/C]

    但是,如果 2 个(甚至更多)竞争 HTTP 请求都在修改 HTTP sessoin 属性 1/A 并且 HTTP 请求/线程 1 想要将属性设置为 1/B 并且竞争 HTTP 请求/线程 2 想要将相同的属性设置为1/C 那么谁赢了?

    好吧,事实证明,最后 1 个获胜,或者更确切地说,最后一个写入 HTTP 会话状态的线程获胜,结果可能是 1/B1/C,这是不确定的,并且受调度的变幻莫测、网络延迟、负载等。事实上,几乎不可能推断出哪一个会发生,更不用说总是发生了。

    虽然我们的匿名用户提供了一些上下文,例如,一个用户同时使用多个设备(网络浏览器,也可能是移动设备......智能手机或平板电脑),但使用单个用户,甚至多个用户重现此类错误用户不是不可能的,但非常不可能。

    但是,如果我们在生产环境中考虑这一点,例如,您可能有数百个 Web 应用程序实例,分布在多台物理机、虚拟机或容器等上,由一些网络负载平衡器进行负载平衡/设备,然后加上当今许多 Web 应用程序是“单页应用程序”、高度复杂的非哑(不再是瘦)但具有 JavaScript 和 AJAX 调用的胖客户端这一事实,然后我们开始了解这种情况更多可能,尤其是在高负载的 Web 应用程序中;想想亚马逊或 Facebook。考虑到 Web 应用程序可以进行的所有动态异步调用,不仅有许多并发用户,而且单个用户的许多并发请求。

    不过,正如我们的匿名用户所指出的,这并不能成为 Web 应用程序开发人员负责任地设计和编码我们的 Web 应用程序的借口。

    一般来说,我会说 HTTP 会话应该只用于跟踪非常少的(即数量)和必要的信息,以保持良好的用户体验并在用户转换时保持用户和应用程序之间的正确交互Web 应用程序的不同部分或阶段,例如跟踪偏好或项目(在购物车中)。通常,不应使用 HTTP 会话来存储“事务性”数据。这样做会让自己陷入困境。 HTTP 会话应该主要是一个读取繁重的数据结构(而不​​是写入繁重),特别是因为 HTTP 会话可以并且很可能会被多个线程访问。

    当然,不同的后备数据存储(如 Redis,甚至 GemFire)都提供锁定机制。 GemFire 甚至提供缓存级别的事务,这在处理由 HTTP 会话对象管理的 Web 交互时非常繁重且不合适(不要与事务混淆)。甚至锁定也会给应用程序带来严重的争用和延迟。

    无论如何,所有这些都是说您非常需要注意交互和数据访问模式,否则您会发现自己陷入困境,所以要小心,永远!

    值得深思!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-30
      • 1970-01-01
      • 1970-01-01
      • 2015-03-21
      • 2017-11-20
      • 2011-03-18
      相关资源
      最近更新 更多