【问题标题】:Solution to the long running query problem in a web application (asynchronous request)Web应用程序中长时间运行的查询问题的解决方案(异步请求)
【发布时间】:2010-12-31 01:25:21
【问题描述】:

问题来了

企业 Web 应用程序的用户正在执行一项任务,该任务会导致很长(非常长)的数据库查询(或其他长时间的处理密集型任务)

问题:

  • 请求超时 - 一段时间后,用户可能会收到请求超时
  • 会话超时 - 如果不使用会话保持方法,可能会发生会话超时
  • 请求线程锁
    • 由于请求线程没有返回,它可能会阻塞新的请求(如果达到池限制)
    • 在某些应用服务器中,服务器的健康状态可能会触发节点或应用程序的强制重启(由于请求线程长时间运行)
  • 如果用户离开页面:
    • 交易未被取消 - 导致无用的处理没有人会从中受益
    • 用户完成后无法返回查看结果
  • 没有进度指示 - 用户只是等待页面刷新

我想出了几个解决方案,但我不确定我是否知道哪个更好(在所有方面,性能、最佳实践、优雅和可维护性),我想知道您推荐的解决方案是什么,并且如果有我错过的解决方案? (可能是的,而且很多)

糟糕的解决方案:将请求线程用作工作线程,在会话中保存进度状态,在另一个并行请求中通过 AJAX 调用检查状态(在会话中)

折衷解决方案:创建您自己的线程池,处理监控线程、工作线程并通过在分布式事务缓存或持久存储中同步状态来处理集群。这会释放请求,但会创建应用程序服务器不知道的线程,并且不会在取消部署时关闭。以干净的方式关闭线程取决于您,并且您总是有可能最终泄漏一些东西。这也不是 J2EE 的做法。

J2EE 解决方案:将 JMS 用于异步任务,这就是它的目的

Spring 解决方案:使用 Spring 批处理

你会在你的项目中做什么/做了什么?您还知道哪些其他解决方案?我上面提到的哪一个是您认为的赢家?

【问题讨论】:

    标签: web-applications jakarta-ee


    【解决方案1】:

    我会结合你所谓的“坏解决方案”和“j2ee 解决方案”:

    1. 原UI线程向“后端”发送异步JMS消息并返回。
    2. 后端接收异步请求并进行处理。
    3. 当后端到达结果(或错误)时,它会返回到控制器层。
    4. 仍在执行 AJAX 轮询或使用 Bayeux/Cometed 的 UI 接收并显示结果。

    诀窍是匹配请求/响应对。我会这样做:

    1. 创建一个“AsyncManagerService”(AMS),它具有 SESSION,甚至可能是 APPLICATION 范围,以便所有线程都与同一个实例通信。
    2. AMS 拥有一个以 id 为键、任何对象为值的映射。
    3. 在创建请求 JMS 消息时,生成一个唯一的随机密钥并将其放入消息的 jmsCorrelationId 以及映射中,并以 NULL 作为值。还将该 ID 传递给 UI。
    4. 只要映射中的值为NULL,就让 UI 线程使用先前生成的 id 轮询 AMS。
    5. 当结果准备好后,让您的 JMS 接收器将其放入 AMS 的地图中的给定 ID。
    6. 下次 UI 线程轮询地图时,它将收到答案并停止轮询。

    这是干净的,并且从任何具体域中抽象出来。纯技术解决方案。

    即使您不喜欢轮询,HTTP 在设计上也是无状态的,我认为这种方式轮询只会在明确定义的时间间隔内发生。

    不管怎样,我用这种模式实现了一个系统,它运行得很好......

    【讨论】:

      【解决方案2】:

      我之前使用的解决方案涉及Jetty Cometd,而不是AJAX。 Ajax 和 Cometd 之间的主要区别在于 Cometd 可以使用更多的 pub/sub 模型 - 客户端(在这种情况下是 Web 浏览器)订阅发布者(您的应用程序),并且应用程序将更新和通知推送到 Web 浏览器作为与 Web 浏览器不断轮询服务器的 ajax 模型相对应。即使您不使用 jetty,您也可以使用 Cometd 解决方案 - 您可以将 jetty jar 放入您各自 Web 服务器的 lib 文件夹中,您应该一切顺利。

      【讨论】:

        【解决方案3】:

        向用户显示“您的请求已被接受,需要一个小时才能更新”的消息

        您创建一个存储所有这些事务的表并在服务器上批量处理这些事务。

        用户不必等待很长时间,他会很高兴看到该消息。交易处理完毕后,您可以发送确认电子邮件。

        这是我认为最好的解决方案。

        【讨论】:

          【解决方案4】:

          这些查询运行了多长时间?

          如果您正在谈论会话到期,也许最好让用户不要等待它。用户在该查询的选项卡中每隔一段时间等待 20 分钟达到峰值总是很烦人的。

          因此,如果确实是很长的查询,也许最好更改 UI 方法并让用户“订购”一个查询,以便他(她)稍后返回查看,甚至在准备好时通过邮件通知.

          在这种情况下,我会在数据库中准备好查询并缓存在一个单独的表中。意思是,我会让网络服务器工作一次以注册请求,让数据库任务或单独的进程/服务器根据请求准备查询并通知用户,然后让网络服务器在用户回来时显示它们结果。

          【讨论】:

          • 所有建议的解决方案都涵盖了这一点,显然后台会有一些服务器进程,并通知用户。问题是使用哪种特定技术(准确地说是 Java 技术)是最佳实践
          • @Ehrann:如果等待时间真的很长,我建议通知是“离线”的,我建议在数据库工作中完成。
          猜你喜欢
          • 1970-01-01
          • 2011-02-07
          • 1970-01-01
          • 2022-10-14
          • 1970-01-01
          • 2016-05-23
          • 2011-01-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多