【问题标题】:How can an EJB parallelize a long, CPU intensive process?EJB 如何并行处理一个长的、CPU 密集型的进程?
【发布时间】:2010-01-05 12:25:00
【问题描述】:

应用程序有一个 CPU 密集型长进程,当前在客户端请求时串行运行在一台服务器上(一种 EJB 方法)。

理论上可以(从概念的角度来看)将该进程拆分为 N 个块并并行执行它们,只要所有并行作业的输出可以在将其发送回客户端之前收集并连接在一起启动了这个过程。我想使用这种并行化来优化性能。

如何使用 EJB 实现这种并行化?我知道我们不应该在 EJB 方法中创建线程。相反,我们应该发布消息(每个作业一个)以供消息驱动 bean (MDB) 使用。但是这样就不再是同步调用了。在这种情况下,同步似乎是一项要求,因为我需要收集所有作业的输出,然后再将其发送回客户端。

有解决办法吗?

【问题讨论】:

    标签: java architecture ejb parallel-processing


    【解决方案1】:

    有很多方法可以做到这一点。

    第一,您可以使用 EJB Timer 创建一个运行一次的进程,该进程将立即启动。这是在后台生成进程的好方法。 EJB 计时器与特定的会话 Bean 实现相关联。您可以将 EJB 计时器添加到您希望能够执行此操作的每个会话 Bean,或者您可以拥有一个会话 Bean,然后可以通过某种调度机制调用您的应用程序逻辑。

    对我来说,我将一个可序列化的参数块连同一个符合特定接口的类名传递给一个通用会话 Bean,然后执行该类。这样我就可以轻松地为大多数东西设置背景。

    关于 EJB 定时器的一个警告是 EJB 定时器是持久的。创建 EJB 计时器后,它会一直留在容器中,直到其作业完成或取消。问题在于,如果您有一个长时间运行的进程,并且服务器出现故障,那么当它重新启动时,该进程将继续并重新启动。请注意,这可能是一件好事,但前提是您的流程准备好重新启动。但是,如果您有一个简单的流程遍历“10,000 项”,如果服务器在第 9,999 项上出现故障,那么当它恢复时,您可以轻松地看到它只是从第 1 项开始。这一切都是可行的,只是需要注意的。

    另一种后台处理方式是您可以使用 JMS 队列。将消息放入队列,处理程序会与应用程序的其余部分异步运行。

    这里的聪明之处是,我也利用 Timer Bean 所做的工作是,您可以根据您配置系统拥有的 MDB 实例的数量来控制将运行多少“作业”。

    因此,对于在多个并行块中运行进程的特定任务,我将任务分解为“片段”,然后将每个片段发送到 MDB 执行它们的消息队列中。如果我允许 10 个 MDB 实例,我可以让任何任务的 10 个“部分”同时运行。

    这实际上工作得非常好。将进程拆分并通过 JMS 队列路由它有一点开销,但这基本上都是“启动时间”。一旦开始,您将获得真正的好处。

    使用消息队列的另一个好处是您可以让实际长时间运行的进程在单独的机器上执行,或者您可以轻松地创建一个机器集群来处理这些进程。然而,界面是一样的,代码不知道有什么区别。

    我发现,一旦您将长时间运行的进程置于后台,您可能会付出无法立即访问该进程的代价。也就是说,没有理由直接监视正在执行的类本身,只需让它们将有趣的信息和统计信息发布到数据库或 JMX 或其他任何东西,而不是拥有可以直接监视对象的东西,因为它共享相同的内存空间。

    我能够轻松设置一个框架,让任务在 EJB 计时器或 MDB 分散队列上运行,任务是相同的,我可以监控它们的进度、停止它们等。

    您可以结合分散技术来创建多个 EJB 计时器作业。 MDB 的免费优势之一是它充当了一个线程池,可以限制您的工作(因此您不会突然因过多的后台进程而使系统饱和)。您只需利用容器中的 EJB 管理功能即可“免费”获得此功能。

    最后,Java EE 6 为 Session Bean 方法提供了一个新的“异步”(或其他)限定符。我不知道它是如何工作的细节,因为我还没有使用新的 Java EE 6 容器。但我想您可能不会只想为这个设施更换容器。

    【讨论】:

      【解决方案2】:

      这个问题已经多次出现,我总结一下有几种可能的解决方案,我只推荐其中一种。

      使用来自 commonj API 的 WorkManager。它允许在 Java EE 容器中托管线程,并且专为适合您的用例而设计。如果您使用的是 WebSphere 或 WebLogic,那么这些 API 已经在您的服务器中可用。对于其他人,您必须自己安装第三方解决方案。

      WorkManager info

      相关问题 Why Spawning threads is discouraged

      【讨论】:

      • 看来WorkManager 是特定于WebSphere 的,所以一般来说不合适:stackoverflow.com/questions/9026516/…
      • 这不是指 commonj WorkManager,而是早于它的 IBM 规范解决方案。正如我已经提到的,commonj 不是 IBM 特定的,并且在大多数平台上都可用。
      【解决方案3】:

      EJB 最终是提供请求/回复语义的客户端-服务器系统的事务组件。如果您发现自己需要在请求/回复周期的范围内对长期运行的事务进行分类,那么您的系统架构师(ure)在某个地方走错了路。

      您描述的情况由带有消息传递后端的基于事件的架构干净且正确地处理。初始事件启动流程(然后可以通过让工作人员订阅事件主题来轻松并行化),并且聚合流程本身在完成时引发一个事件。您仍然可以在请求/回复周期的范围内压缩这些序列,但是您必然会违反 Java EE 系统架构规范的文字和精神。

      【讨论】:

      • 这是一个非常黑白的世界观,我们大多数人都必须处理灰色阴影。您不希望仅仅为了提供此功能而重新架构整个系统。更不用说在完成多个并行操作之前进行同步调用并没有本质上的错误。这本身并不能证明基于事件的架构优于基于简单请求/回复的架构。
      • 您在考虑到此类操作而指定和设计的框架或平台的上下文中描述的内容没有任何问题。但这不是 JEE 平台。 JEE 的重点是采用约束来促进分发和可用性(以及现在被遗忘的面向组件的目标)。一旦你开始打破限制,你实际上就走上了一条自我挫败的道路。因此,我们必须在拒绝滥用技术作为教条观点(“黑白”)的观点上存在分歧。
      • "request/reply semantics... 一个带有消息后端的​​基于事件的架构" 如果请求语义是“执行长期过程”并且回复是“长期过程完成”,我明白你的意思”。但是,如果请求意味着“开始长期进程”并且回复是“长期进程排队”,它是否适合 JEE 模型?然后,正如另一个答案所暗示的那样,EJB 可以简单地使用 JMS 发送消息,以让 MDB(甚至消息的非 JEE 使用者)完成工作。
      • 缺乏对长时间运行的进程的支持是 JEE 架构中长期存在的差距(MDB 没有解决这个问题),但公平地说,我不清楚如何协调透明分布、COA 和具有此类元素的要求苛刻的 SLA。 JEE 通过连接器架构整体解决了这个问题,并且大概您将提供处理长时间运行的进程等的事务执行引擎 (EIS)。
      【解决方案4】:

      回到未来 - Java EE 7 通过 ManagedThreadFactory、ManagedExecutor 服务等 (JSR 236: Concurrency Utilities for Java EE) 提供更多并发支持,您可以使用它们创建自己的“托管”线程。它不再是禁忌在 EE AS 中通过使用 ManagedThread* API 来支持它(Wildfly?)

      更多细节

      https://jcp.org/aboutJava/communityprocess/ec-public/materials/2013-01-1516/JSR236-EC-F2F-Jan2013.pdf http://docs.oracle.com/javaee/7/tutorial/doc/concurrency-utilities002.htm

      【讨论】:

      • 最后我需要使用 JBoss AS 7.1 并面临无线程并行执行的任务,我自动遵循基于消息的架构,其中包含一个主任务拆分为子任务;使用 Jboss 和 HorneQ 集群和负载均衡;发布在 GitHub 上的框架
      • JBoss AS 7.1 已经实现了 EJB 3.1 规范。您可以将异步方法调用用于简单的用例。不要忘记在应用服务器上配置线程池以满足您的需求。
      【解决方案5】:

      我曾经参与过一个 EJB 事务一次运行长达 5 小时的项目。啊!

      同样的应用程序也有一位 BEA 专家顾问,他批准他们从交易中启动额外的线程。虽然在规范和其他地方不推荐它,但它不会自动导致失败。您需要注意,您的额外线程超出了容器的控制范围,因此如果出现问题,那是您的错。但是如果你能保证在最坏情况下启动的线程数不超过合理的限制,并且它们都在合理的时间内干净地终止,那么很有可能像这样工作。事实上,在你的情况下,这听起来几乎是唯一的解决方案。

      有一些稍微深奥的解决方案可能是您的 EJB 应用程序向另一个应用程序寻求服务,然后在返回给 EJB 调用者之前自行执行多线程处理。但这实际上只是在转移问题。

      但是,您可以考虑使用线程池解决方案来保持产生的线程数的上限。如果你有太多线程,你的应用程序会表现得很糟糕。

      【讨论】:

      【解决方案6】:

      您已经很好地分析了这种情况,不,没有与 EJB 模型匹配的模式。

      创建线程主要是被禁止的,因为它绕过了应用程序。服务器线程管理策略也因为事务

      我在一个具有类似要求的项目上工作,我决定产生额外的线程(当时违反了 sepc)。并行化的操作是只读的,因此它对事务起作用(线程基本上没有与它们关联的事务)。我还知道每次 EJB 调用不会产生太多线程,因此线程数不是问题。但是如果你的线程应该修改数据,那么你就严重破坏了 EJB 的事务模型。但如果你的操作是纯计算,那可能没问题。

      希望对你有帮助...

      【讨论】:

      • 从 CommonJ API 中查找 WorkManager。它允许您在托管线程中执行此操作。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-13
      • 1970-01-01
      • 2011-06-03
      • 2020-06-16
      • 2019-11-08
      • 2013-07-31
      • 2021-08-15
      相关资源
      最近更新 更多