【问题标题】:Correct way to use EJB Asynchronous methods使用 EJB 异步方法的正确方法
【发布时间】:2016-01-13 10:16:35
【问题描述】:

我有两个任务需要执行,例如 task1task2,它们是同一业务流程的一部分。当task1 完成时,我必须对最终用户做出响应,因此必须尽量缩短响应时间。

我目前的方法是执行task1,并在task1 完成后立即异步调用task2 方法。 task2 很复杂,它的响应时间超出了我的控制,因为它有一些外部依赖性。

@Stateless
public class SessionBean1 {

    @Inject
    SessionBean2 sessionBean2;

    public void doTask1(){
        // task one stuff
        sessionBean2.doTask2();
    }

}



@Stateless
public class SessionBean2 {

    @Asynchronous
    public void doTask2(){
        // do task2 stuff
    }

}

在 websphere 8.0(使用中的 EJB 容器)中,同步方法和异步方法由不同的线程池运行。

我最初的假设是,即使task2 表现不佳,task1 也不会产生影响,但遗憾的是这不是真的。

如果task2 性能不佳,异步线程池中的所有线程都将被占用。这将导致task1 等待异步线程空闲,因此task1 会产生影响。

websphrere 服务器日志中的消息: The request buffer for thread pool WorkManager.WebSphere_EJB_Container_AsynchMethods_Internal_WorkManager has reached its capacity

我的问题是什么是实现我在这里想要实现的目标的正确方法。

【问题讨论】:

  • 如果您使用的是 Java EE 7,则可以使用 @AccessTimeout(value = xx) 注释,但我认为 Websphere 是 Java EE 6 ?
  • @mattfreake:与链接的image 一样,异步方法请求的数量是有限的,并且取决于异步线程的数量。我可以增加线程数,但即使几分钟 task2 表现不佳,我的 task1 仍然需要等待。线程数也受硬件配置限制。
  • 如果线程池大小有问题,使用@rjdkolb 建议的JMS 队列的想法会更好。
  • @SteveC:目前我正在将task2 的方法参数添加到ConcurrentLinkedQueue,然后在Timer 的帮助下定期轮询它们并调用task2 方法。到目前为止,它已经证明是好的。使用 JMS 可能有点过头了,因为 task1task2 在同一台服务器上。
  • 所有 Java EE 实现都“内置”了 JMS,因此可以在“同一服务器”中运行。

标签: java jakarta-ee asynchronous ejb websphere


【解决方案1】:

另一种选择是增加管理控制台中“EJB 异步方法调用设置”的“工作请求队列大小”。这是一个队列,在实际线程池本身之前,所以这可能会为您争取更多时间。

理想情况下,这应该与上面建议的超时结合使用。

【讨论】:

  • 这似乎是一个不错的选择。默认情况下,它由运行时处理,并且取决于分配的线程数,但我不知道有多少。 websphrere 管理控制台The runtime currently uses the larger of 20 and the value of maximum number of threads. 的最后一行是否有意义?
  • 查看您的图像,我将其视为“15 或 20 中的较大者”,因此在您的情况下应该为 20。您可以保持 15 的最大线程池大小,并增加队列到更大的东西。我希望队列应该有一个小的内存/处理器占用空间,这样它就可以存储您对 task2 的等待请求,同时让现有的请求完成并保持在您的硬件限制内。但是,如果 task2s 可以无限期地完成,那么我认为超时将是要走的路
【解决方案2】:

我认为@AccessTimeout 是您正在寻找的。我看到一个例子here 这将限制 .doTask2() 可以运行的时间并避免您的问题。

@Stateless
public class SessionBean1 {

    @Inject
    SessionBean2 sessionBean2;

    public void doTask1(){
        // task one stuff
        sessionBean2.doTask2();
    }

}

SessionBean2

@Stateless
public class SessionBean2 {
    @AccessTimeout(60000)//default timeunit is TimeUnit.MILLISECONDS
    @Asynchronous
    public void doTask2(){
        // do task2 stuff
    }

}

作为替代方案:

要限制异步进程可以花费的时间,请使用 handle.get(xx, TimeUnit.xx);方法。您还需要返回 Future 而不仅仅是 void 以使其工作。

我希望这适合您的用例,因为您需要调用 .get

@Stateless
public class SessionBean1 {

    @Inject
    SessionBean2 sessionBean2;

    public void doTask1(){
        // task one stuff
        Future<Void> handle = sessionBean2.doTask2();
        // do other stuff
        handle.get(10, TimeUnit.SECONDS);//If you want to block later

    }

}

SessionBean2

@Stateless
public class SessionBean2 {

    @Asynchronous
    public Future<Void> doTask2(){
        // do task2 stuff
        new AsyncResult<Void>(Void);
    }

}

【讨论】:

  • 我将在添加@AccessTimeout 后加载测试并让您知道结果。但在调用实际超时的情况下,我的 task2 将不会被调用。
  • 如果您希望它是异步的并且始终运行,不如将消息放入 JMS 队列
  • ...或者使用 EJB 计时器,这可能更容易使用。
猜你喜欢
  • 1970-01-01
  • 2016-09-29
  • 2013-05-23
  • 1970-01-01
  • 2015-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多