EJB 如何并行化冗长的 CPU 密集型进程?

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

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

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

有没有解决方案?


答案 1

有各种各样的方法可以做到这一点。

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

对于我来说,我将一个可序列化的参数 blob 以及一个满足特定接口的类名传递给一个通用 Session Bean,然后由该会话 Bean 执行该类。通过这种方式,我可以轻松背景化大多数内容。

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

另一种设置背景的方法是可以使用 JMS 队列。将消息放在队列中,处理程序将与应用程序的其余部分同步运行。

这里的聪明之处在于,我也利用定时器Bean的工作做了一些事情,即您可以根据您配置系统要拥有的MDB实例来控制将运行多少“作业”。

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

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

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

我发现,一旦你将一个长时间运行的进程降级到后台,你就可以付出代价,让这个过程不那么即时地访问。也就是说,没有理由直接监视执行类本身,只是让它们将有趣的信息和统计信息发布到数据库或JMX,或者其他任何东西,而不是拥有可以直接监视对象的东西,因为它共享相同的内存空间。

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

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

最后,Java EE 6 为 Session Bean 方法提供了一个新的“异步”(或类似)限定符。我不知道这是如何工作的细节,因为我还没有玩过一个新的Java EE 6容器。但我想你可能不想仅仅为了这个设施而改变容器。


答案 2

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

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

工作经理信息

相关问题 为什么不鼓励生成线程


推荐