【问题标题】:JavaEE - EJB/CDI Method Duration MechanismJavaEE - EJB/CDI 方法持续时间机制
【发布时间】:2017-11-13 12:49:42
【问题描述】:

不确定如何命名这个问题,但希望描述可以提供更好的解释。我正在寻找一种方法来注释 ejb 方法或 cdi 方法,使用自定义注释(如“@Duration”或其他东西 aaand 以便在给定持续时间之后花费太长时间时终止方法执行。我想一些伪代码会让一切变得清晰:

public class myEJBorCdiBean {

@Duration(seconds = 5)
public List<Data> complexTask(..., ...)
{
  while(..)
  // this takes more time than the given 5 seconds so throw execption
}

总而言之,一个方法花费的时间非常长,它会抛出一个给定的持续时间过期错误或类似的东西

有点超时机制,不知道有没有这样的东西,我是javaEE世界的新手。

提前谢谢大家

【问题讨论】:

  • this similar thread 中,提出了事务超时作为一种解决方案,对我来说它非常适合您的用例。它们不是标准的,但几乎存在于所有应用服务器中。

标签: java jakarta-ee ejb cdi


【解决方案1】:

您不应该在 EJB/CDI 容器中使用线程 API。 EJB 规范明确指出:

企业 bean 不得尝试管理线程。企业 bean 不得尝试启动、停止、挂起或恢复线程,或 更改线程的优先级或名称。企业 bean 不得 尝试管理线程组。

托管 bean 及其业务方法的调用必须完全由容器控制,以避免损坏其状态。根据您的用例,或者将此操作卸载到专用服务(javaee 之外),或者您可以使用 EJB @SingletonSchedule 提出一些半黑客解决方案 - 这样您就可以定期检查一些控制标志。如果你在 Wildfly/JBoss 上运行,你可能会误用 @TransactionTimeout 注释——因为默认情况下 EJB 方法是事务感知的,在 Transaction 上设置超时将有效控制 bean 方法的调用超时。我不确定其他应用服务器如何支持它。

如果异步处理是一个选项,那么 EJB @Asynchronous 可能会有所帮助:请参阅 Asynchronous tutorial - Cancelling and asynchronous operation

一般建议:不要在 EJB/CDI 中运行长时间运行的操作。每个请求都会产生一个新线程,线程是有限的资源,您的应用程序将更难以扩展和维护(长时间运行的操作 ~= 状态),如果您的服务器在方法调用期间崩溃会发生什么,用例将如何在集群中工作环境。同样很难说,在不了解您的用例的情况下,什么是更好的方法,但请研究 java EE 批处理 api、带有消息驱动 bean 的 JMS 或带有 @Asynchronous 的异步处理

【讨论】:

  • 为什么在 ejb javaee 世界中没有这样的设计解决方案?我的意思是我不是第一个想在方法上设置持续时间的人?它必须用线程来解决......你有代码解决方案吗?
  • 这是一个架构决策,因为服务器/容器管理组件状态、调用、链接、事务、拦截器、上下文... EJB 和 CDI 规范为服务器管理的 bean 定义了规则和特性——即为什么你不能使用线程 API。您仍然没有提及您的用例,但我猜您正在混合客户端和服务器上的超时处理。当您终止您的方法时,您究竟期望发生什么? - 是否是错误,是否处于正常状态,所涉及的资源应该如何处理。任何在没有@Asynchronous 的情况下处理它的代码都将是一个 hack。
  • 我的意思是为什么没有持续时间> 5秒后抛出异常之类的东西......这是有用的功能。错误应该发生而不是等待几分钟完成
【解决方案2】:

这是一个非常有意义的想法——将复杂的任务限制在一定的执行时间。在实际的网络计算中,当复杂的搜索任务的持续时间超过最大可接受的时间时,许多用户将不愿意等待它完成。

Enterprise 容器控制线程池,以及活动线程之间的 CPU 资源分配。它还考虑了耗时的 I/O 任务(通常是磁盘访问)期间的保留时间。

尽管如此,对启动任务变量进行编程是有意义的,因此在复杂任务期间不时验证该特定任务的持续时间。我建议您编写一个本地的、可运行的任务,该任务从作业队列中选择计划任务。我在 Glassfish 下运行的 Java Enterprise 后端应用程序有这方面的经验。

首先是接口定义Duration.java

// Duration.java 
@Qualifier 
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Documented 
@Retention(RetentionPolicy.RUNTIME) 
public @interface Duration {
    public int minutes() default 0; // Default, extended from class, within path 
}

现在遵循作业的定义TimelyJob.java

// TimelyJob.java    
@Duration(minutes = 5)
public class TimelyJob {

    private LocalDateTime localDateTime = LocalDateTime.now();
    private UUID uniqueTaskIdentifier;
    private String uniqueOwnerId;

    public TimelyJob(UUID uniqueTaskIdentifier, String uniqueOwnerId) {
        this.uniqueTaskIdentifier = uniqueTaskIdentifier;
        this.uniqueOwnerId = uniqueOwnerId;
    }

    public void processUntilMins() {
        final int minutes = this.getClass().getAnnotation(Duration.class).minutes();
        while (true) {
            // do some heavy Java-task for a time unit, then pause, and check total time
            // break - when finished
            if (minutes > 0 && localDateTime.plusMinutes(minutes).isAfter(LocalDateTime.now())) {
                break;
            }
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                System.err.print(e);
            }
        }
        // store result data in result class, 'synchronized'  access
    }

    public LocalDateTime getLocalDateTime() {
        return localDateTime;
    }

    public UUID getUniqueTaskIdentifier() {
        return uniqueTaskIdentifier;
    }

    public String getUniqueOwnerId() {
        return uniqueOwnerId;
    }        
}

执行定时作业的 Runnable 任务 - TimedTask.java - 实现如下:

// TimedTask.java
public class TimedTask implements Runnable {

    private LinkedBlockingQueue<TimelyJob> jobQueue = new LinkedBlockingQueue<TimelyJob>();

    public void setJobQueue(TimelyJob job) {
        this.jobQueue.add(job);
    }

    @Override
    public void run() {
        while (true) {
            try {
                TimelyJob nextJob = jobQueue.take();
                nextJob.processUntilMins();
                Thread.sleep(100);
            } catch (InterruptedException e) {
                System.err.print(e);
            }
         }
      } 
   }

在单独的代码中,TimedTask 的注视

   public void initJobQueue() {
       new Thread(new TimedTask()).start();
   }

这个功能实际上是在 Java 中实现了一个批处理作业调度器,使用注解来控制结束任务的时间限制。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-04
    • 2013-07-25
    • 2012-05-09
    • 2015-11-23
    相关资源
    最近更新 更多