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

发布于 2024-11-09 18:59:06 字数 500 浏览 4 评论 0原文

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

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

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

有什么想法吗?

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

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

I have the following situation. I have a job that:

  • May time out after a given amount of time, and if so occurs needs to throw an exception
  • If it does not time out, will return a result
  • If this job returns a result, it must be returned as quickly as possible, because performance is very much an issue. Asynchronous solutions are hence off the table, and naturally tying up the system by hammering isn't an option either.
  • Lastly, the system has to conform to the EJB standard, so AFAIK using ordinary threads is not an option, as this is strictly forbidden.

Our current solution uses a thread that will throw an exception after having existed for a certain amount of time without being interrupted by an external process, but as this clearly breaks the EJB standard, we're trying to solve it with some other means.

Any ideas?

Edited to add: Naturally, a job which has timed out needs to be removed (or interrupted) as well.

Edited to add 2:
This issue doesn't seem to have any solution, because detecting a deadlock seems to be mostly impossible sticking to pure EJB3 standards. Since Enno Shioji's comments below reflect this, I'm setting his suggestion as the correct answer.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

傻比既视感 2024-11-16 18:59:06

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

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

//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 方法根本不会超时而被迫使用线程,则会出现线程泄漏。如果它在较长时间后超时,因此您想“快捷”地回复用户,这将是我考虑使用线程的唯一情况,就像我想象的那样使用它。这可能会导致线程在负载下堆积,我会努力避免这种情况。

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

另外,你的客户端界面是什么?听起来像是同步网络服务之类的?


In that case, if I were you I would use a HashedWheelTimer as a singleton... this mechanism should work great with your requirement (here is an implementation). However, this unfortunately seem to conflict with the ban on threading AND the ban on singleton in the EJB spec. In reality though there really isn't a problem if you would do this. See this discussion for example. We have also used the singleton pattern in our EJB app. which used JBoss. However, if this isn't a viable choice then I might look at isolating the processing in its own JVM by defining a new web service (and deploy it in a web-container or something), and call that service from the EJB app. This would however obviously incur performance hit and now you would have another whole new app.

This is more like a request for clarification, but it's too long to fit as a comment..

I'm not sure how you are doing it right now, since from what you wrote, just using the request processing thread seems to be the way to go. Like this:

//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");
    }
}

I'm not sure why you are creating threads at all. If you are forced to use threads because the getResult method doesn't timeout at all, you would have a thread leak. If it timeouts after a longer time and thus you want to "shortcut" your reply to the user, that would be the only case I'd consider using a thread like I imagine how you are using it. This could result in Threads piling up under load and I'd strive to avoid such situation.

Maybe you can post some code and let us know why you are creating in your service at all?

Also, what's your client interface? Sounds like it's a synchronous webservice or something?


In that case, if I were you I would use a HashedWheelTimer as a singleton... this mechanism should work great with your requirement (here is an implementation). However, this unfortunately seem to conflict with the ban on threading AND the ban on singleton in the EJB spec. In reality though there really isn't a problem if you would do this. See this discussion for example. We have also used the singleton pattern in our EJB app. which used JBoss. However, if this isn't a viable choice then I might look at isolating the processing in its own JVM by defining a new web service (and deploy it in a web-container or something), and call that service from the EJB app. This would however obviously incur performance hit and now you would have another whole new app.

攒眉千度 2024-11-16 18:59:06

使用 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- 中使用可选的 cmt-timeout-in-seconds 元素ejb-jar.xml

With Bean Managed Transaction, the timeout for the specific transaction can be specified by using UserTransaction interface.

Modify the timeout value that is
associated with transactions started
by the current thread with the begin
method.

void setTransactionTimeout(int seconds) throws SystemException
  • Transaction will timeout after specified seconds & may not get propagated further. If exception is not thrown implicitly, then can throw it explicitly based on the result.
  • Will return a result on successful completion within specified time.
  • Can use it with stateless session beans so there may not be a performance issue.
  • Its EJB standard so that will not be an issue to implement.

With little-bit work around, it should work fine in the given scenario.

Edit : Also can use server specific properties to manage transaction timeout.

JBoss : At either at class or method level annotation @TransactionTimeout(100) can be applied.

Weblogic : Specifying the parameters in weblogic-ejb-jar.xml

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

GlassFish : Using the optional cmt-timeout-in-seconds element in sun-ejb-jar.xml

轻拂→两袖风尘 2024-11-16 18:59:06

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

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

是的,我认为这是一个“黑客”,但它满足了要求,而且是可移植的。

Stick the process and it's timeout thread in to a class annotated with @WebService, put that class in to a WAR, then invoke the WebService from your EJB.

WARs don't have the same limitations or live under the same contract that EJBs do, so they can safely run threads.

Yes, I consider this a "hack", but it meets the letter of the requirements, and it's portable.

笑饮青盏花 2024-11-16 18:59:06

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

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

由于我不知道您的实际平台,因此您必须自己在 google commonj 上搜索您的平台 8-)

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

注意:这不是实际标准,但它广泛适用于不同平台,并且应该非常适合您的目的。

You can create threads using the commonj WorkManager. There are implementations built into WebSphere and Weblogic as they proposed the standard, but you can also find implementations for other appservers as well.

Basically, the WorkManager allows you to create managed threads inside the container, much like using an Executor in regular Java. Your only other alternative would be to use MDB's, but that would be a 'heavier' solution.

Since I don't know your actual platform, you will have to google commonj with your platform yourself 8-)

Here is a non IBM or Oracle solution.

Note: This is not an actual standard, but it is widely available for different platforms and should suit your purposes nicely.

染柒℉ 2024-11-16 18:59:06

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

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

For EJBs, there is a concept of "Container Managed Transactions". By specifying @TransactionAttribute on your bean, or specific method, the container will create a transaction when ever the method(s) are invoked. If the execution of the code takes longer than the transaction threshold, the container will throw an exception. If the call finishes under the transaction threshold, it will return as usual. You can catch the exception in your calling code and handle it appropriately.

For more on container managed transactions, check out: http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/Transaction3.html and http://download.oracle.com/javaee/5/tutorial/doc/bncij.html

杀お生予夺 2024-11-16 18:59:06

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

You could use @TimeOut. Something like:

@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);
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文