首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >TomEE占用了太多的@异步操作

TomEE占用了太多的@异步操作
EN

Stack Overflow用户
提问于 2013-07-31 19:31:25
回答 2查看 1.5K关注 0票数 3

我正在使用Apache 1.5.2JAX-RS,这几乎是开箱即用的,带有预定义的TomEE。

以下是简化的代码。我有一个用于接收信号的REST式接口:

代码语言:javascript
复制
@Stateless
@Path("signal")
public class SignalEndpoint {
    @Inject
    private SignalStore store;

    @POST
    public void post() {
        store.createSignal();
    }
}

接收信号会引发很多事情。存储将创建一个实体,然后触发一个异步事件。

代码语言:javascript
复制
public class SignalStore {
    @PersistenceContext
    private EntityManager em;

    @EJB
    private EventDispatcher dispatcher;

    @Inject
    private Event<SignalEntity> created;

    public void createSignal() {
        SignalEntity entity = new SignalEntity();
        em.persist(entity);
        dispatcher.fire(created, entity);
    }
}

dispatcher非常简单,它的存在仅仅是为了使事件处理异步。

代码语言:javascript
复制
@Stateless
public class EventDispatcher {
    @Asynchronous
    public <T> void fire(Event<T> event, T parameter) {
        event.fire(parameter);
    }
}

接收事件是另一回事,它从信号中派生数据,存储它,并触发另一个异步事件:

代码语言:javascript
复制
@Stateless
public class DerivedDataCreator {
    @PersistenceContext
    private EntityManager em;

    @EJB
    private EventDispatcher dispatcher;

    @Inject
    private Event<DerivedDataEntity> created;

    @Asynchronous
    public void onSignalEntityCreated(@Observes SignalEntity signalEntity) {
        DerivedDataEntity entity = new DerivedDataEntity(signalEntity);
        em.persist(entity);
        dispatcher.fire(created, entity);
    }
}

对此做出反应甚至是实体创建的第三层。

总之,我有一个REST调用,它同步地创建一个SignalEntity,它异步地触发一个DerivedDataEntity的创建,它异步地触发第三种类型的实体的创建。所有这些操作都很完美,存储过程也是完美的解耦。

除了当我以编程方式触发了很多(f.e。1,000)循环中的信号.取决于我的AsynchronousPool大小,在处理信号(相当快)的数量约为该大小的一半后,应用程序将完全冻结几分钟。然后,它继续,处理大约相同数量的信号,相当快,然后再冻结。

在过去的半个小时里,我一直在玩AsynchronousPool设置。例如,将其设置为2000,可以很容易地使我的所有信号一次处理,而不被冻结。但在那之后,这个系统也不健全。触发另外1000个信号,结果它们被创建得很好,但是整个派生数据的创建从未发生过。

现在我完全不知所措了。当然,我可以摆脱所有这些异步事件,自己实现某种队列,但我总是认为EE容器的意义在于解除这种乏味。异步EJB事件应该已经带来了自己的队列机制。当队列排得太满时不应该冻结的。

有什么想法吗?

更新:

我现在已经用1.6.0-快照进行了尝试。它的行为有点不同:它仍然不起作用,但我确实得到了一个例外:

代码语言:javascript
复制
Aug 01, 2013 3:12:31 PM org.apache.openejb.core.transaction.EjbTransactionUtil handleSystemException
SEVERE: EjbTransactionUtil.handleSystemException: fail to allocate internal resource to execute the target task
javax.ejb.EJBException: fail to allocate internal resource to execute the target task
    at org.apache.openejb.async.AsynchronousPool.invoke(AsynchronousPool.java:81)
    at org.apache.openejb.core.ivm.EjbObjectProxyHandler.businessMethod(EjbObjectProxyHandler.java:240)
    at org.apache.openejb.core.ivm.EjbObjectProxyHandler._invoke(EjbObjectProxyHandler.java:86)
    at org.apache.openejb.core.ivm.BaseEjbProxyHandler.invoke(BaseEjbProxyHandler.java:303)
    at <<... my code ...>>
    ...
Caused by: java.util.concurrent.RejectedExecutionException: Timeout waiting for executor slot: waited 30 seconds
    at org.apache.openejb.util.executor.OfferRejectedExecutionHandler.rejectedExecution(OfferRejectedExecutionHandler.java:55)
    at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:821)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1372)
    at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:132)
    at org.apache.openejb.async.AsynchronousPool.invoke(AsynchronousPool.java:75)
    ... 38 more

这就好像TomEE不会对操作进行任何排队。如果没有线程可以在调用的时刻自由处理,那就倒霉了。当然,这不是故意的..?

更新2:

好的,我似乎无意中发现了一个半解决方案:将AsynchronousPool.QueueSize属性设置为maxint解决了冻结问题。但问题依然存在:为什么QueueSize一开始就这么有限,更令人担忧的是:为什么这会阻碍整个应用程序?如果队列满了,它会阻塞,但是一旦任务从它中取出,另一个任务就会弹出,对吗?队列似乎被阻塞,直到它再次完全为空。

更新3:

任何想要尝试的人:http://github.com/JanDoerrenhaus/tomeefreezetestcase

更新4:

事实证明,增加队列大小并不能解决这个问题,它只是延迟了它。问题仍然是一样的:一次异步操作太多,TomEE阻塞得太厉害,以至于它甚至不能在终止时取消应用程序部署。

到目前为止,我的诊断是任务清理不能正常工作。我的任务都是非常小和快速(请参阅github的测试用例)。我已经担心OpenJPA或HSQLDB会减缓太多并发调用,但是我注释掉了所有的em.persist调用,问题仍然是一样的。因此,如果我的任务非常小,而且速度很快,但仍然能够阻止TomEE,以至于30秒后它无法获得任何进一步的任务(javax.ejb.EJBException: fail to allocate internal resource to execute the target task),那么我可以想象,已经完成的任务仍在徘徊,堵塞了管道。

我怎样才能解决这个问题?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-08-05 15:44:18

基本上,BlockingQueues使用锁来确保数据的一致性,并避免数据丢失,因此在高度并发的环境中,它将拒绝许多任务(您的情况)。

您可以在主干上使用RejectedExecutionHandler实现重试以提供任务。一个实现可以是:

代码语言:javascript
复制
new RejectedExecutionHandler() {
        @Override
        public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) {
            for (int i = 0; i < 10; i++) {
                if (executor.getQueue().offer(r)) {
                    return;
                }

                try {
                    Thread.sleep(50);
                } catch (final InterruptedException e) {
                    // no-op
                }
            }
            throw new RejectedExecutionException();
        }
    }

它甚至更好地与随机睡眠(在最小和最大之间)。

其基本思想是:如果队列已满,请稍候一小段时间以减少并发性。

票数 2
EN

Stack Overflow用户

发布于 2014-02-16 07:36:42

可通过WEB/application.properties进行配置。属性https://issues.apache.org/jira/browse/TOMEE-1012

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17979259

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档