私はApache TomEE 1.5.2 JAX-RSを使用しています.
以下は単純化されたコードです。シグナルを受信するための 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);
}
}
それに反応することは、エンティティ作成の第 3 層ですらあります。
要約すると、同期的に を作成する REST 呼び出しがありSignalEntity
、非同期的に の作成をDerivedDataEntity
トリガーし、3 番目のタイプのエンティティの作成を非同期的にトリガーします。それはすべて完璧に機能し、ストレージプロセスは美しく分離されています.
forループで多くのシグナル(fe 1000)をプログラムでトリガーする場合を除きます。私のAsynchronousPool
サイズにもよりますが、そのサイズの約半分の信号を (非常に高速に) 処理した後、アプリケーションは数分間完全にフリーズします。その後、再びフリーズする前に、ほぼ同じ量の信号を非常に高速に処理するために再開します。
AsynchronousPool
過去30分間、設定をいじっていました。たとえば、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 があまりにもひどく詰まり、終了時にアプリケーションをアンデプロイすることさえできなくなります。
これまでのところ、私の診断では、タスクのクリーンアップが正しく機能していません。私のタスクはすべて非常に小さくて高速です ( github のテスト ケースを参照してください)。同時呼び出しが多すぎると OpenJPA または HSQLDB の速度が低下するのではないかとすでに心配していましたが、すべての呼び出しをコメントアウトしましたがem.persist
、問題は同じままでした。したがって、タスクが非常に小さくて高速であるにもかかわらず、TomEE をブロックして 30 秒後にそれ以上のタスクを取得できなかった場合 ( javax.ejb.EJBException: fail to allocate internal resource to execute the target task
)、完了したタスクが長引いて、いわばパイプを詰まらせていると想像できます。 .
この問題を解決するにはどうすればよいですか?