我正在使用Apache TomEE 1.5.2 JAX-RS,它非常开箱即用,具有预定义的HSQLDB。
以下是简化代码。我有一个REST风格的接口来接收信号:
@Stateless
@Path("signal")
public class SignalEndpoint {
@Inject
private SignalStore store;
@POST
public void post() {
store.createSignal();
}
}
接收信号会触发很多东西。商店将创建一个实体,然后触发一个异步事件。
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);
}
}
调度程序非常简单,只是存在使事件处理异步。
@Stateless
public class EventDispatcher {
@Asynchronous
public <T> void fire(Event<T> event, T parameter) {
event.fire(parameter);
}
}
接收事件是另一回事,它从信号中获取数据,存储它,并触发另一个异步事件:
@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
的创建,异步触发创建第三种类型的实体。这一切都很完美,存储过程也很好地分离。
除了我以编程方式触发for循环中的大量(f.e. 1000)信号时。根据我的AsynchronousPool
大小,在处理大约一半大小的信号(非常快)之后,应用程序会完全冻结长达几分钟。然后它重新开始,在再次冻结之前快速处理大约相同数量的信号。
过去半小时我一直在玩AsynchronousPool
设置。例如,将其设置为2000可以轻松地立即处理所有信号,而不会冻结。但在此之后,该系统也不健全。触发另外1000个信号,导致它们被创建得很好,但是从未发生过派生数据的整个创建。
现在我完全不知道该怎么做。我当然可以摆脱所有这些异步事件并自己实现某种队列,但我一直认为EE容器的重点是让我摆脱这种乏味。异步EJB事件应该已经带来了自己的队列机制。一旦队列太满,就不应该冻结。
有什么想法吗?
更新
我现在用1.6.0-SNAPSHOT尝试过它。它的行为有点不同:它仍然不起作用,但我确实得到了一个例外:
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严重不足,甚至无法在终止时取消部署应用程序。
到目前为止,我的诊断是任务清理无法正常工作。我的任务都非常小而且快(参见test case on github)。我已经担心OpenJPA或HSQLDB会减慢太多并发呼叫的速度,但我注释掉了所有em.persist
个呼叫,问题仍然存在。因此,如果我的任务非常小而且速度很快,但仍然设法阻止TomEE如此糟糕以至于在30秒(javax.ejb.EJBException: fail to allocate internal resource to execute the target task
)之后无法完成任何进一步的任务,我会想象完成的任务会延续,堵塞管道,可以这么说。
我该如何解决这个问题?
答案 0 :(得分:2)
基本上BlockingQueues使用锁来确保数据的一致性并避免数据丢失,因此在太高的并发环境中它会拒绝很多任务(你的情况)。
您可以使用RejectedExecutionHandler实现在trunk上播放以重试提供任务。一种实现可以是:
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();
}
}
随机睡眠(在最小和最大之间)它甚至可以更好地工作。
这个想法基本上是:如果队列已满,请等待一段时间以减少并发性。
答案 1 :(得分:1)
可通过WEB-INF / application.properties https://issues.apache.org/jira/browse/TOMEE-1012
进行配置