1

我正在将 Spring MVC 3 + Tiles 用于 web 应用程序。我的操作很慢,我想要一个请稍候页面。

我知道有两种主要的方法来请等待页面:

  1. 长期请求:渲染并刷新页面的“请稍候”位,但在操作完成之前不要完成请求,此时您可以使用一些 javascript 流出剩余的响应以重定向或更新页面。
  2. 立即返回,并在后台线程上开始处理。客户端轮询服务器(在 javascript 中,或通过页面刷新),并在后台线程完成时重定向。

(1) 很好,因为它使操作全部保持单线程,但对于 Tiles 似乎不可能,因为每个 JSP 必须在页面组装并返回给客户端之前完成完整呈现。

所以我开始实施(2)。在我的实现中,第一个请求使用 Spring 的@Async注释在后台线程上启动操作,该注释返回一个Future<Result>. 然后它会向用户返回一个“请稍候”页面,该页面每隔几秒刷新一次。

当请等待页面刷新时,控制器需要检查后台线程的进度。这样做的最佳方法是什么?

  1. 如果我Future直接将对象放入 Session 中,那么轮询请求线程可以将其拉出并检查线程的进度。但是,这是否意味着我的 Sessions 不可序列化,所以我的应用程序不能部署在多个 Web 服务器上(不需要粘性会话)?
  2. 我可以在 Session 中放置某种状态标志,并让后台线程在 Session 完成时更新它。我非常担心将 HttpSession 对象传递给非请求线程会导致难以调试的错误。这是允许的吗?任何人都可以引用任何文件吗?当然,当会话在内存中时它工作得很好,但是如果会话存储在数据库中呢?如果我有多个网络服务器怎么办?
  3. 我可以在我的数据库中放置某种状态标志,键入会话 ID,或慢速操作的其他方面。在我的域数据库中而不是在会话中有会话数据似乎很奇怪,但至少我知道数据库是线程安全的。
  4. 我还有其他选择吗?
4

1 回答 1

1

您问题的 Spring MVC 部分相当简单,因为该问题与 Spring MVC 无关。在此答案中查看可能的解决方案:https ://stackoverflow.com/a/4427922/734687

正如您在代码中看到的那样,作者正在使用 tokenService 来存储未来。不包括实施,正如您已经知道的,当您需要故障转移时,问题就开始了。

  • 不可能序列化未来并让它跳转到第二个服务器实例。线程在某个实例内执行,因此必须留在那里。所以会话存储是没有选择的。
  • 如示例链接中所示,您可以使用令牌服务。这通常只是一个 HashMap,您可以在其中存储对象并稍后通过令牌(字符串标识符)再次访问它。但同样,这仅适用于同一个 Web 应用程序,当 tokenService 是单例时。

解决方案不是保存未来,而是保存工作状态(工作中、已完成、结果失败)。即使查询会话和执行线程在不同的机器上,状态也应该是可访问和可序列化的。但是你会怎么做呢?这可以通过将其存储在数据库或文件系统(上面的示例中,您可以检查 zip 文件是否可用)或键/值存储或缓存或通用对象存储(Terracota)中来实现, ...

事实上,每个批处理框架(例如 Spring Batch)都是这样工作的。它将作业的当前状态存储在数据库中。您担心将域数据与操作数据混合在一起。但大多数应用程序都这样做。在大型应用程序中,可以使用两个数据库实例,操作数据和域数据。

因此,我建议您将工作的状态和结果保存在数据库中。
希望有帮助。

于 2012-03-13T17:17:39.047 回答