【问题标题】:Long running tasks in message-driven beans and timeouts消息驱动 bean 中的长时间运行任务和超时
【发布时间】:2015-06-20 20:06:50
【问题描述】:

我想使用 MDB(小时)处理一些运行时间很长的任务,但是 MDB 由 JMS 支持,它有一些超时(我认为在 tomEE 中大约 10 分钟)。

更糟糕的是,工作也是持久的。

由于我无法在 MDB 上捕获超时异常(可以吗?),我正在考虑手动处理 JMS 使用者的选项。手动处理它可以通过两种方式完成:同步或异步。

我将采用异步方式回退到实现 MessageListener 的无状态 bean,然后我将无法再次处理超时。

同步地,我想,我可以处理 catch 块中的超时,但是,如何在 TomEE+ 中实现一个实例/工作者/任何东西的池,它们正在监听队列,等待作业处理? (记住,这里的超时不是等待消息出现在队列中的时间,而是执行长时间运行任务的时间)

【问题讨论】:

  • 为什么不将长时间运行的进程分叉到一个单独的对象中?
  • 如果有办法控制这些单独对象的池大小,那可能是一种办法。你想试试答案吗? :-)

标签: java ejb jms message-driven-bean apache-tomee


【解决方案1】:

为什么不将长时间运行的进程分叉到一个单独的对象? > 如果有办法控制这些单独对象的池大小,它可能是一种方法。你想试试答案吗? :-)

您不希望对象随着时间的推移不受控制地增长吗?嗯......我可以看到你要去哪里。

我唯一能想到的就是使用并发框架中的 FixedThreadPool。生成一个线程来执行长时间运行的任务,并确保 MDB 在移交工作后立即返回。通过在测试环境中分析您的应用程序来控制线程池。

Weblogic 和 Websphere 等应用程序服务器为您提供了 WorkManager 等复杂框架来完成此类任务。

【讨论】:

    【解决方案2】:

    尝试将使用 JMS 消息和长时间运行的处理任务分开

    MDB 可以从队列中获取消息并调用无状态会话 bean Asynchronous Method Invocation

    【讨论】:

    • 如果我在 SLSB 中创建一个新事务,我会摆脱 MDB 超时吗?
    • AFAIK 否。MDB 只会在 SLSB 完成后返回。
    • @ramp - 嗯。异步调用创建新事务。来自 EJB 3.1 规范。 4.5.3 事务 客户端事务上下文不会通过异步方法调用传播。从 Bean Developer 的角度来看,从来没有从客户端流入的事务上下文。这意味着,例如,异步方法上的 REQUIRED 事务属性的语义与 REQUIRES_NEW 完全相同。
    • @user3145155 - 你是对的。我正在回答 OP 关于 SLSB 的问题,但在您的回答中错过了异步方法调用部分。本质上,这与我在回答中概述的解决方案相同。这在 EJB 3.1 中是现成的。我已经编辑了我的答案以删除有关此解决方案可行性的评论,并且还支持您的答案。
    【解决方案3】:

    ManagedExecutorService 存在于 JEE7 中,用于处理异步任务的执行。通过利用Future 接口实现(请参阅FutureTask),也可以使任务可取消。以下仅为示例,请勿 1:1 复制并在生产中使用。它在未来的映射中存在内存泄漏,UUID 永远不会被清理。

    import java.util.Map;
    import java.util.UUID;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.Future;
    import java.util.concurrent.RunnableFuture;
    
    import javax.annotation.Resource;
    import javax.enterprise.concurrent.ManagedExecutorService;
    import javax.enterprise.context.ApplicationScoped;
    
    @ApplicationScoped
    public class AsyncTaskManager {
    
        @Resource
        private ManagedExecutorService mes;
    
        private Map<String, Future> futureByUUID = new ConcurrentHashMap<>();
    
        /**
         * Launch the provided Future implemented task.
         * @param runnable The runnable implementation to execute.
         * @param <R> The type of the task result returned.
         * @return The UUID that the executing ask is mapped to.
         */
        public <R> String launch (RunnableFuture<R> runnable) {
            String uuid = UUID.randomUUID().toString();
            mes.submit(runnable);
            futureByUUID.put(uuid, runnable);
            return uuid;
        }
    
        /**
         * Retrieve a future instance by it's UUID.
         * @param uuid The uuid of the future.
         * @param <T> The type of the future's result.
         * @return The future instance.
         */
        @SuppressWarnings("unchecked")
        public <T> Future<T> getByUUID (String uuid) {
            return (Future<T>)futureByUUID.get(uuid);
        }
    }
    

    有一整套接口和类可以组合起来改变执行器服务中任务的行为。例如,ManagedTask 接口提供对其中一些行为的访问。

    ManagedExecutorService 被用于@Asynchronous EJB 方法注释,如MGolovanov 的回答中所述。任务创建和 Future 实例创建由 EJB 框架处理。

    【讨论】:

      猜你喜欢
      • 2019-01-09
      • 1970-01-01
      • 1970-01-01
      • 2020-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多