我有以下情况。我有一份工作:
- 可能在给定的时间后超时,如果发生这种情况,则需要引发异常
- 如果没有超时,将返回一个结果
- 如果此作业返回结果,则必须尽快返回,因为性能在很大程度上是一个问题。因此,异步解决方案是不可能的,通过锤击自然地捆绑系统也不是一种选择
- 最后,系统必须符合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);
}
}