应用程序有一个CPU密集型长进程,当客户端请求时,它会在一个服务器(EJB方法)上串行运行。
理论上可以(从概念的角度来看)将该进程拆分为N个块并并行执行它们,只要可以收集并连接所有并行作业的输出,然后再将其发送回客户端启动了这个过程。我想使用这种并行化来优化性能。
如何使用EJB实现此并行化?我知道我们不应该在EJB方法中创建线程。相反,我们应该发布消息驱动bean(MDB)消息(每个作业一个)。但那时它不再是同步通话了。在这种情况下,同步似乎是一个要求,因为我需要在将所有作业的输出发送回客户端之前收集它们的输出。
有解决方法吗?
答案 0 :(得分:10)
有各种各样的方法可以做到这一点。
首先,您可以使用EJB Timer创建一个立即启动的一次运行进程。这是在后台生成进程的好方法。 EJB Timer与特定的Session Bean实现相关联。您可以将EJB计时器添加到您希望能够执行此操作的每个会话Bean,也可以使用单个会话Bean,然后可以通过某种调度机制调用您的应用程序逻辑。
对我来说,我传递了一个可序列化的blob参数以及一个符合特定接口的类名,然后执行该类的通用会话Bean。通过这种方式,我可以轻松地查看大部分内容。
关于EJB Timer的一个警告是EJB Timers是持久的。创建EJB计时器后,它将保留在容器中,直到其作业完成或取消。问题在于如果你有一个长时间运行的进程,并且服务器出现故障,当它重新启动时,进程将继续并重新启动。请注意,这可能是一件好事,但前提是您的流程已准备好重新启动。但是如果你有一个简单的过程迭代“10,000项”,如果服务器在第9,999项下降,当它恢复时,你可以很容易地看到它只是从第1项开始。这一切都是可行的,只是需要注意的警告的。
另一种背景的方法是你可以使用JMS队列。在队列中放置一条消息,处理程序从应用程序的其余部分运行。
这里的聪明部分,以及利用Timer Bean的工作,我可以根据您配置系统的MDB实例数量来控制运行的“作业”数量。
因此,对于以多个并行块运行进程的特定任务,我执行任务,将其分解为“片段”,然后在消息队列中发送每个片段,MDB在其中执行它们。如果我允许10个MDB实例,我可以同时运行任何任务的10个“部分”。
这实际上效果出奇的好。将进程分解并通过JMS队列进行路由会产生一些开销,但这基本上都是“启动时间”。一旦它开始,你就会获得真正的好处。
使用Message Queue的另一个好处是,您可以在单独的计算机上执行实际长时间运行的进程,或者您可以轻松创建一组计算机来处理这些进程。然而,界面是相同的,代码不知道差异。
我发现,一旦你将长时间运行的流程降级到后台,你就可以为那个流程的即时访问权限付出代价。也就是说,没有理由直接监视执行类本身,只是让它们向数据库,JMX或其他任何东西发布有趣的信息和统计信息,而不是直接监视对象,因为它共享相同的内存空间。 / p>
我很容易设置一个框架,允许任务在EJB Timer或MDB分散队列上运行,任务是相同的,我可以监控他们的进度,阻止他们等等。
您可以结合使用分散技术来创建多个EJB Timer作业。 MDB的一个免费优势是它充当线程池,可以限制您的工作(因此您不会因为太多的后台进程而突然使您的系统饱和)。只需通过利用容器中的EJB管理功能,您就可以“免费”获得此功能。
最后,Java EE 6为Session Bean方法提供了一个新的“异步”(或其他)限定符。我不知道它是如何工作的细节,因为我还没有使用新的Java EE 6容器。但我想你可能不想仅为这个设施更换容器。
答案 1 :(得分:6)
这个特殊问题已经出现过多次,我将总结出几种可能的解决方案,其中只有一种我会推荐。
使用commonj API中的WorkManager。它允许Java EE容器中的托管线程,并且专门设计用于适合您的用例。如果您使用的是WebSphere或WebLogic,则这些API已在您的服务器中可用。对于其他人,您必须自己设置第三方解决方案。
答案 2 :(得分:4)
EJB最终是提供请求/回复语义的客户端 - 服务器系统的事务组件。如果您发现自己处于需要在请求/回复周期范围内对长时间运行的事务进行归档的情况,那么您的系统架构师(某处)已经走错了路。
您描述的情况由具有消息传递后端的基于事件的体系结构干净且正确地处理。初始事件启动进程(然后通过让工作者订阅事件主题可以简单地并行化)并且聚合进程本身在完成时引发事件。您仍然可以在请求/回复周期的范围内挤压这些序列,但您必然会违反Java EE系统架构规范的字母和精神。
答案 3 :(得分:2)
回到未来 - Java EE 7通过ManagedThreadFactory,ManagedExecutor服务等(JSR 236:Java EE的并发实用程序)提供了更多的并发支持,您可以使用它来创建自己的“托管”线程。它不再是禁忌在EE AS支持它(Wildfly?)通过使用ManagedThread * API的
更多详情
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
答案 4 :(得分:1)
我曾经参与过EJB事务一次最多运行5个小时的项目。 AARGH!
同一个应用程序还有一位BEA专家顾问,他们批准他们从交易中启动了额外的线程。虽然它在规格和其他地方被推荐,但它并不会自动导致失败。你需要知道你的额外线程是在容器的控制之外,因此如果出现问题,那就是你的错。但是如果你能确保在最坏的情况下启动的线程数不超过合理的限制,并且它们都在合理的时间内干净地终止,那么很可能像这样工作。事实上,在你的情况下,它听起来几乎是唯一的解决方案。
有一些稍微深奥的解决方案可能会使您的EJB应用程序到达服务的另一个应用程序,然后在返回EJB调用者之前执行多线程处理。但这基本上只是改变了问题。
但是,您可以考虑使用线程池解决方案来保持生成的线程数的上限。如果你有太多的线程,你的应用程序将表现得非常糟糕。
答案 5 :(得分:1)
你已经很好地分析了这种情况,不,没有与EJB模型相匹配的模式。
主要禁止创建线程,因为它绕过应用程序。服务器线程管理策略以及交易。
我参与了一个有类似需求的项目,我决定产生额外的线程(然后反对sepc)。并行化的操作是只读的,因此它对事务有效(线程基本上没有与它们相关的事务)。我也知道每次EJB调用都不会产生太多线程,因此线程数不是问题。但是如果您的线程应该修改数据,那么您就会严重破坏EJB的事务模型。但如果您在纯计算中操作,那可能没问题。
希望它有所帮助...