【问题标题】:Cancelling an @Asynchronous EJB call取消 @Asynchronous EJB 调用
【发布时间】:2011-06-14 06:59:38
【问题描述】:

我正在编写一个企业 Java 应用程序,它使用异步 EJB 3.1 方法来并行执行多个任务。为了支持取消长时间运行的任务,我一直在尝试使用 Future 接口。

不幸的是,从客户端应用程序调用 future.cancel(true) 似乎对执行任务的 bean 的会话上下文没有影响,尽管取消调用正在返回 true

我有一个简单的界面:

public interface AsyncInterface
{
    Future<Integer> run() throws Exception;
}

使用bean实现如下:

@Stateless
@Remote(AsyncInterface.class)
public class AsyncBean
{
    @Resource SessionContext myContext;

    @Asynchronous
    public Future<Integer> run() throws Exception
    {
        Integer result = 0;

        System.out.println("Running AsyncBean");

        while(myContext.wasCancelCalled() == false)
        {
            Thread.sleep(2000);
            System.out.println("Working");
        }      

        System.out.println("AsyncBean cancelled");

        return new AsyncResult<Integer>(result);
     }
}

客户端代码很简单:

InitialContext ctx = new InitialContext();
AsyncInterface async = (AsyncInterface)ctx.lookup("AsyncBean/remote");
Future<Integer> future = async.run();

if( future.cancel(true) )
{
     System.out.println("future.cancel() returned true");
}
else
{
     System.out.println("future.cancel() returned false");
}

bean 的输出是源源不断的“Working”;它永远不会检测到取消。

如果相关,我将在 JBoss Application Server 6.0.0 上运行该应用程序。我没有找到太多使用 Future 接口的取消功能的示例代码,所以我想知道我是否正确使用了 Future。这种用法看起来正确吗?取消异步 EJB 方法调用是否有更好的选择?

【问题讨论】:

  • 这是一种奇怪的行为,因为如果取消方法返回 true,则表明您的会话 bean 方法将不会被调用(原因是当您调用取消时,该方法可能已经在运行)。您可以在 EJB 3.1 规范的第 3.4.8.1.1 节中查看更多信息。
  • 将此链接到类似问题,但适用于 glassfish stackoverflow.com/q/16493381/3701228

标签: java asynchronous jakarta-ee ejb ejb-3.1


【解决方案1】:

回答我自己的问题 - 我发现 JBoss AS 6.0.0 不支持调用异步请求。

作为替代方案,我已重构代码以使用 JMS 请求/响应样式消息传递。

消息驱动 bean 用于执行异步操作。消息驱动 bean 创建一个临时队列并通过 JMS 将该队列返回给调用者。这是通过利用 JMS 消息的 replyTo 字段来实现的。

然后,消息驱动 bean 会定期检查临时队列是否有取消消息​​。

这比 @Asynchronous 版本更复杂,但它适用于 JBoss AS 6.0.0。

【讨论】:

    【解决方案2】:

    您必须 return Future 类型的对象,而不是 null。顺便问一下,你没有拿到 NPE 吗?我希望你的代码有问题。

    此外,不应调用cancel()See the docs here。顺便说一句,根据docs,不应该调用的不仅仅是cancel()方法,也不应该调用任何实例方法。这就引出了一个问题,为什么一个方法应该返回AsyncResult。我根本不知道,可能它在那里以备将来使用。但是class level comments 建议所有方法都在那里,以便将结果提供给容器。

    因此,我不确定是否可以取消异步 EJB 调用。

    [稍作研究后编辑]

    试试这个实现,

    @Stateless
    @Remote(AsyncInterface.class)
    public class AsyncBean
    {
        @Resource SessionContext myContext;
    
        @Asynchronous
        public Integer run() throws Exception
        {
            Integer result = 0;
    
            System.out.println("Running AsyncBean");
    
            while(myContext.wasCancelCalled() == false)
            {
                Thread.sleep(2000);
                System.out.println("Working");
            }      
    
            System.out.println("AsyncBean cancelled");
    
            return result;
         }
    }
    

    【讨论】:

    • 是的,我应该返回一个 Future 而不是 null。我已更改代码以返回 AsyncResult(Future 的实现)。它没有导致空指针异常,因为代码永远不会超过 while 循环。结果没有改变,仍然没有检测到取消。
    • @malloc:你有没有试过在你的future.cancel(true) 之前打印一些东西?只是看看该行是否被执行。
    • @Adeel:是的,future.cancel(true) 正在执行并返回 true。我已经编辑了原始问题中的代码以包含更改。
    • @Adeel: async.run() 调用涉及两个期货。一个(Future 类型)立即返回,第二个(AsyncResult 类型)在调用结束时返回。返回 AsyncResult 将结果设置在第一个 Future 上,可以使用 future.get() 检索。允许在第一个 Future 上调用取消。请参阅此处的文档download.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/…
    • @malloc:正如你所说,有 2 个Futures 涉及,但是在哪里?我看不见。立即返回的那个在哪里?谁在退货?什么情况?因为,Future 是一个接口,这意味着返回的必须是其实现的实例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-02
    • 2012-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多