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

时间:2011-05-26 17:26:27

标签: java multithreading timeout ejb-3.0

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

  • 可能会在一段时间后超时,如果发生则需要抛出异常
  • 如果没有超时,将返回结果
  • 如果此作业返回结果,则必须尽快返回,因为性能非常重要。因此异步解决方案不在桌面上,并且通过锤击自然地绑定系统也不是一种选择。
  • 最后,系统必须符合EJB标准,因此使用普通线程的AFAIK不是一种选择,因为这是严格禁止的。

我们当前的解决方案使用一个线程,该线程在存在一段时间之后会抛出异常,而不会被外部进程打断,但是由于这显然打破了EJB标准,我们试图用其他一些解决它装置

有什么想法吗?

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

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

6 个答案:

答案 0 :(得分:1)

使用Bean Managed Transaction,可以使用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元素

答案 1 :(得分:1)

这更像是一个澄清请求,但它太长了,不适合作为评论..

我不知道你现在是怎么做的,因为从你写的内容来看,使用请求处理线程似乎是要走的路。像这样:

//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作为单身人士......这种机制应该适合您的要求(here is an implementation)。但是,遗憾的是,这似乎与EJB规范中禁止线程和禁止单例的禁令相冲突。实际上,如果你这样做,确实没有问题。例如,请参阅this discussion。我们还在EJB应用程序中使用了单例模式。用过JBoss。但是,如果这不是一个可行的选择,那么我可能会考虑通过定义一个新的Web服务(并将其部署在Web容器或其他东西中)来隔离其自己的JVM中的处理,并从EJB应用程序调用该服务。然而,这显然会导致性能下降,现在你将有另一个全新的应用程序。

答案 2 :(得分:1)

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

WAR没有相同的限制或与EJB相同的合同生效,因此可以安全地运行线程。

是的,我认为这是一个“黑客”,但它符合要求的字母,并且它是可移植的。

答案 3 :(得分:0)

您可以使用commonj WorkManager创建线程。 WebSphereWeblogic内置了实现,因为他们提出了标准,但您也可以找到其他应用服务器的实现。

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

由于我不知道你的实际平台,你必须自己google commonj与你的平台8 - )

这是non IBM or Oracle solution.

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

答案 4 :(得分:0)

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

有关容器管理的交易的详情,请查看:http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/Transaction3.htmlhttp://download.oracle.com/javaee/5/tutorial/doc/bncij.html

答案 5 :(得分:0)

您可以使用@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);
  }
}