在不使用线程的情况下处理EJB3中的超时



我有以下情况。我有一份工作:

  • 可能在给定的时间后超时,如果发生这种情况,则需要引发异常
  • 如果没有超时,将返回一个结果
  • 如果此作业返回结果,则必须尽快返回,因为性能在很大程度上是一个问题。因此,异步解决方案是不可能的,通过锤击自然地捆绑系统也不是一种选择
  • 最后,系统必须符合EJB标准,因此不能选择使用普通线程的AFAIK,因为这是严格禁止的

我们当前的解决方案使用了一个线程,该线程在存在一定时间后将抛出异常,而不会被外部进程中断,但由于这显然违反了EJB标准,我们正试图用其他方法来解决它。

有什么想法吗?

编辑添加:当然,超时的作业也需要删除(或中断)。

已编辑以添加2:这个问题似乎没有任何解决方案,因为按照纯EJB3标准,检测死锁似乎几乎是不可能的。由于恩野希吉在下面的评论反映了这一点,我将他的建议作为正确答案。

这更像是一个澄清请求,但太长了,无法作为注释。。

我不确定你现在是怎么做的,因为从你写的内容来看,仅仅使用请求处理线程似乎是一种方法。像这样:

//Some webservice method (synchronous)
public Result process(Blah blah){
    try{
        return getResult(TimeUnit.SECONDS, 10);
    }catch(InterruptedException e){
        //No result within 10 seconds!
        throw new ServiceUnavailableException("blah");
    }
}

我不知道你为什么要创建线程。如果因为getResult方法根本没有超时而被迫使用线程,那么就会出现线程泄漏。如果它在更长的时间后超时,因此你想"快捷"回复用户,这将是我唯一会考虑使用线程的情况,就像我想象的那样。这可能会导致线程在负载下堆积,我会努力避免这种情况。

也许你可以发布一些代码,让我们知道你为什么要在你的服务中创建?

此外,您的客户端界面是什么?听起来像是一个同步的Web服务还是什么?


在这种情况下,如果我是你,我会使用HashedWheelTimer作为单例。。。这个机制应该能很好地满足您的需求(这里是一个实现)。然而,不幸的是,这似乎与EJB规范中对线程的禁止和对单例的禁止相冲突。实际上,如果你这样做,确实没有问题。例如,请参阅本讨论。我们还在EJB应用程序中使用了singleton模式。其使用JBoss。然而,如果这不是一个可行的选择,那么我可能会考虑通过定义一个新的web服务(并将其部署在web容器或其他地方)来隔离其JVM中的处理,并从EJB应用程序调用该服务。然而,这显然会影响性能,现在你会有另一个全新的应用程序。

使用Bean托管事务,可以使用UserTransaction接口指定特定事务的超时。

修改超时值与已启动的事务关联由当前线程开始方法

void setTransactionTimeout(int seconds) throws SystemException
  • 事务将在指定秒后超时&可能不会进一步传播。若异常不是隐式抛出的,则可以根据结果显式抛出
  • 将在指定时间内成功完成时返回结果
  • 可以将它与无状态会话bean一起使用,因此可能不会出现性能问题
  • 它的EJB标准,所以实现起来不会有问题

只要做一点工作,它在给定的场景中应该可以正常工作。

编辑:还可以使用特定于服务器的属性来管理事务超时。

JBoss:在类或方法级别,可以应用注释@TransactionTimeout(100)

Weblogic:指定Weblogic-ejb-jar.xml 中的参数

<transaction-descriptor>
     <trans-timeout-seconds>100</trans-timeout-seconds> 
</transaction-descriptor>

GlassFish:使用sun-ejb-jar.xml 中的可选cmt-timeout-in-seconds元素

将进程及其超时线程粘贴到用@WebService注释的类中,将该类放入WAR中,然后从ejb调用WebService。

WAR与EJB没有相同的限制,也没有相同的契约,因此它们可以安全地运行线程。

是的,我认为这是一个"破解",但它符合要求,而且是可移植的。

您可以使用commonj WorkManager创建线程。WebSphere和Weblogic在提出标准时内置了一些实现,但您也可以找到其他应用程序服务器的实现。

基本上,WorkManager允许您在容器内创建托管线程,就像在常规Java中使用Executor一样。您唯一的其他选择是使用MDB,但这将是一个"更重"的解决方案。

由于我不知道你的实际平台,你将不得不谷歌commonj与你的平台自己8-)

这是一个非IBM或Oracle的解决方案。

注意:这不是一个实际的标准,但它可以广泛用于不同的平台,应该很适合您的目的。

对于EJB,有一个"容器管理事务"的概念。通过在bean或特定方法上指定@TransactionAttribute,容器将在调用方法时创建事务。如果代码的执行时间超过事务阈值,则容器将抛出异常。如果调用在事务阈值以下完成,它将照常返回。您可以在调用代码中捕获异常并进行适当的处理。

有关容器管理事务的更多信息,请查看:http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/Transaction3.html和http://download.oracle.com/javaee/5/tutorial/doc/bncij.html

您可以使用@TimeOut。类似于:

@Stateless
public class TimedBean {
  @Resource
  private TimerService timerService;
  static private AtomicInteger counter = new AtomicInteger(0);
  static private Map<Integer, AtomicBoolean> canIRunStore = new ...;
  public void doSomething() {
    Integer myId = counter.getAndIncrement();
    AtomicBoolean canIRun = new AtomicBoolean(true);
    canIRunStore.put(myId, canIRun);
    timerService.createTimer(1000, 0, myId);
    while (canIRun.get() /* && some other condition */) {
      // do my work ... untill timeout ... 
    }
  }
  @Timeout
  @PermitAll
  public void timeout(Timer timer) {
    Integer expiredId = (Integer) timer.getInfo();
    AtomicBoolean canHeRun = canIRunStore.get(expiredId);
    canIRunStore.remove(expiredId);
    canHeRun.set(false);
  }
}

相关内容

  • 没有找到相关文章

最新更新