一个EJB如何并行执行长时间的、CPU密集型的过程?

14

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

理论上可以将这个过程分成N个块并并行执行,只要所有并行作业的输出在发送回发起该过程的客户端之前可以被收集和合并。我希望使用这种并行化来优化性能。

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

有解决方案吗?

6个回答

11

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

一种方法是使用EJB定时器来创建一个立即启动的单次运行过程。这是一种在后台生成进程的好技术。EJB定时器与特定的Session Bean实现关联。您可以将EJB定时器添加到每个要执行此操作的Session Bean中,或者您可以拥有一个单独的Session Bean,然后通过某些调度机制调用您的应用逻辑。

对于我而言,我会传递一个可序列化的参数块和一个符合特定接口的类名,然后将其发送到通用Session Bean中,该Bean随后执行该类。这样我就可以轻松地后台处理大部分事情。

EJB定时器的一个注意点是EJB定时器是持久的。一旦您创建了一个EJB定时器,它会一直保留在容器中,直到其工作完成或被取消为止。其中一个问题是,如果您有一个长时间运行的进程,并且服务器关闭,当它重新启动时,进程将继续并继续进行。这可能是件好事,但前提是您的进程准备好重新启动。但如果您只是简单地遍历"10,000个项目"的过程,在服务器在第9999个项目处停止运行时,当它重新启动时,很可能会从项目1重新开始。这都是可以解决的,只是要注意一下。

另一种后台处理的方法是使用JMS队列。将消息放入队列中,然后处理程序从您的应用程序的其余部分异步运行。

聪明之处在于,也利用定时器Bean的工作,您可以根据配置系统拥有的MDB实例数量控制将有多少个"作业"运行。

因此,对于以多个并行块运行进程的特定任务,我接收任务,将其拆分成"片段",然后将每个片段发送到消息队列中,在那里MDB将执行它们。如果允许10个MDB实例,则可以同时运行任何任务的10个"部分"。

这种方式出奇的好用。将进程分解并通过JMS队列路由存在一定的额外开销,但基本上都是"启动时间"。一旦开始运行,你就会获得真正的好处。

使用消息队列的另一个好处是,你可以在单独的机器上执行实际的长时间运行的进程,或者你可以轻松创建一组机器来处理这些进程。然而,接口是相同的,代码并不知道区别。

我发现,一旦将长时间运行的进程调入后台,你可以付出价格,即访问该进程时没有立即响应。也就是说,没有理由直接监视执行类本身,只需让它们发布有趣的信息和统计数据到数据库、JMX或其他地方,而不是像共享同一内存空间的对象那样直接监视对象本身。

我很容易设置一个框架,让任务在EJB Timer或MDB散列队列上运行,任务是相同的,我可以监视它们的进度,停止它们等等。

你可以将散列技术与EJB Timer作业结合起来创建几个作业。 MDB的一个免费好处是它充当线程池,可以调整作业的速度(以避免突然饱和系统过多的后台进程)。通过利用容器中的EJB管理功能,你可以免费获得这个优势。

最后,Java EE6有一个新的"异步"(或其他)修饰符用于会话Bean方法。我不知道它是如何工作的细节,因为我还没有尝试过新的Java EE6容器。但我想你可能不想仅仅为了使用这个设施而更换容器。


6
这个问题已经多次出现,我将总结一下可能的解决方案,其中只有一个是我推荐的。
使用commonj API中的WorkManager。它允许在Java EE容器中管理线程,并专门设计以适应您的用例。如果您正在使用WebSphere或WebLogic,则这些API已经可用于您的服务器。对于其他人,您将不得不自己放置第三方解决方案。 WorkManager信息 相关问题 为什么不鼓励生成线程

似乎WorkManager是WebSphere专用的,因此通常不适用:https://dev59.com/QV_Va4cB1Zd3GeqPWb0l - Raedwald
1
这并不是指通用的 commonj WorkManager,而是 IBM 早期推出的特定解决方案。正如我之前提到的,commonj 不是 IBM 特有的,而且在大多数平台上都可用。 - Robin

4
一个EJB最终是为客户端-服务器系统提供请求/响应语义的事务组件。如果您发现自己需要将长时间运行的事务强制纳入请求/响应周期的范围内,则系统架构师已经走错了方向。
您描述的情况可以通过基于事件的架构和具有消息传递后端的正确处理。初始事件启动进程(然后可以通过让工作人员订阅事件主题轻松并行化),聚合进程本身在完成时会引发一个事件。您仍然可以将这些序列挤入请求/响应周期的范围内,但是必要时会违反Java EE系统架构规范的字母和精神。

1
这是非常二元的世界观,我们大多数人必须处理灰色地带。您不想重新架构整个系统,只是为了提供此功能。更不用说仅仅因为有一个同步调用在所有操作完成后才返回,就固有问题了。这本身并不能证明事件驱动架构比简单的请求/应答架构更合适。 - Robin
在特定上下文中,如果是为此类操作指定和设计的框架或平台,则您所描述的没有任何问题。但这不是JEE平台的情况。JEE的整个重点是采用限制来促进分发和可用性(以及现在被遗忘的组件定向目标)。一旦开始打破这些限制,实际上就已经走上了自我毁灭的道路。因此,在拒绝技术误用作为教条观点(“黑白分明”)的情况下,我们必须保持异议。 - alphazero
“请求/响应语义...基于事件的架构,具有消息后端” 我理解你的观点,如果请求语义是“执行长期进程”,而回复是“长期进程完成”,那么这个模型是否适用于JEE模型呢?如果请求意味着“启动长期进程”,而回复是“长期进程排队”,那么就像另一个答案建议的那样,EJB可以使用JMS发送消息,由MDB(甚至是非JEE消息的消费者)完成工作。 - Raedwald
JEE架构中长时间运行进程的缺乏支持一直存在着一个漏洞(这个问题并没有被MDBs解决),但公平地说,我不清楚如何将透明分布、COA和苛刻的SLAs与这些元素协调起来。总体而言,JEE通过连接器架构来解决这个问题,并且您可能会提供处理长时间运行进程的事务执行引擎(EIS)等。 - alphazero

2
回到未来 - Java EE 7 通过 ManagedThreadFactory、ManagedExecutor 等方式提供了更多的并发支持(JSR 236: 用于 Java EE 的并发工具),您可以使用它们创建自己的“托管”线程。在 EE AS 中已经不再是一个禁忌(通过使用 ManagedThread* API's 在 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上发布了该框架。 - Alex Punnen
JBoss AS 7.1 已经实现了 EJB 3.1 规范。对于简单的用例,您可以使用异步方法调用。不要忘记在应用服务器上配置线程池以满足您的需求。 - JanM

1

我曾经参与过一个项目,其中EJB事务运行时间长达5个小时。天哪!

这个应用程序还有一个BEA专家顾问,他批准从事务中启动额外的线程。虽然规范和其他地方不推荐这样做,但它并不会自动导致失败。您需要知道,您的额外线程在容器之外,因此如果出现问题,那就是您的问题。但是,如果您可以确保最坏情况下启动的线程数不超过合理限制,并且它们都能在合理的时间内干净地终止,那么这种工作方式是完全可行的。实际上,在您的情况下,这似乎是唯一的解决方案。

有一些略微神秘的解决方案可供选择,其中您的EJB应用程序向另一个应用程序请求服务,然后该应用程序在返回给EJB调用者之前进行多线程处理。但这本质上只是将问题转移了。

但是,您可以考虑使用线程池解决方案来限制生成的线程数量。如果线程太多,您的应用程序将表现得非常糟糕。


不推荐,绝对不允许:http://stackoverflow.com/a/9739300/545127 - Raedwald

1

你已经很好地分析了情况,但是没有与EJB模型匹配的此类模式。

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

我曾经参与过一个类似要求的项目,我决定生成额外的线程(违反了规范)。需要并行化的操作是只读的,所以在事务方面它是可行的(线程基本上没有与之关联的事务)。我也知道每个EJB调用不会生成太多的线程,所以线程数不是问题。但是如果您的线程需要修改数据,则会严重破坏EJB的事务模型。但是,如果您的操作是纯计算,那可能没问题。

希望对您有所帮助...


从CommonJ API中查找WorkManager。它允许您在托管线程中执行此操作。 - Robin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接