8.12. Jakarta Enterprise Bean でクラスター化されたデータベースタイマー
JBoss EAP は、クラスター環境で Jakarta Enterprise Beans タイマーを永続化するための、クラスター化されたデータベースベースのタイマーをサポートします。クラスターリングはデータベースで提供されるため、短時間以内にタイマーの数がオフになる場合、パフォーマンスが向上します。refresh-interval を使用し、ejb3/service=timer-service/database-data-store コンポーネントの allow-execution 属性を使用すると、パフォーマンスを最適化することができます。
以下のように、クラスター化されていないモードでデータベースタイマーを使用することもできます。
-
refresh-intervalを0に設定します。 - すべてのノードに対して一意のパーティション名を指定するか、各ノードに別のデータベースを使用します。
Jakarta Enterprise Beans-clustered データベースタイマーは以下のように機能します。
- タイマーの実行が許可されるすべてのノードは、認識しているすべてのタイマーに対してタイムアウトをスケジュールします。
このタイムアウトが期限切れになると、各ノードでタイマーの状態を更新してタイマーをロックしようとします。
状態を更新するクエリーは、以下のクエリーに似ています。
UPDATE JBOSS_EJB_TIMER SET TIMER_STATE=? WHERE ID=? AND TIMER_STATE<>? AND NEXT_DATE=?;
トランザクションと READ_COMMITTED または SERIALIZABLE の分離モードのために、1 つのノードのみが行の更新で成功し、タイマーが実行するノードになります。
8.12.1. Jakarta Enterpise Beans でクラスター化されたタイマーの設定
データベースベースのタイマーストアを追加して、Jakarta Enterprise Beans-clustered タイマーを設定できます。
要件
- データベースは READ_COMMITTED または SERIALIZABLE 分離モードをサポートしている必要があります。
手順
データベースでサポートされるタイマーストアを作成します。
/subsystem=ejb3/service=timer-service/database-data-store=my-clustered-store:add(allow-execution=true, datasource-jndi-name='java:/MyDatasource', refresh-interval=60000, database='postgresql', partition='mypartition')
以下のパラメーターを設定します。
-
allow-execution:trueに設定して、このノードがタイマーを実行できるようになります。falseに設定すると、JBoss EAP はこのノードのタイマーを別のノードで実行するデータベースに追加します。タイマー実行をクラスター内の複数のノードだけに制限すると、データベース全体の負荷が軽減されます。 -
datasource-jndi-name: 使用するデータソース。 refresh-interval: このノードは、他のノードが追加する新しいタイマーについてデータベースをチェックする前に経過する必要のある期間を設定します。値はミリ秒単位です。重要値を小さく設定すると、JBoss EAP はタイマーを高速に選択し、データベースの負荷が増えることを意味します。操作が失敗したか、
allow-executionが false であるため、タイマーを追加したノードが実行できない場合は、ノードが更新されるまでこのタイマーは実行されません。Database: 使用中のデータベースのタイプを定義します。一部の SQL ステートメントはデータベースによってカスタマイズされます。この属性を定義しない場合、サーバーは自動的にタイプを検出しようとします。現時点では、サポートされているタイプは
postgresql、mysql、oracle、db2、hsql、およびh2です。SQL は
modules/system/layers/base/org/jboss/as/ejb3/main/timers/timer-sql.propertiesファイルにあります。このファイルに新しいデータベース固有の SQL ステートメントを追加すると、実行される SQL を変更したり、新しいデータベースのサポートを追加したりできます。
-
partition: このノードを所属させるパーティションの名前に設定します。同じパーティションのノードからタイマーのみがこのノードに表示されます。パフォーマンスを向上させるために、この属性を使用して大規模なクラスターを複数の小さなクラスターに分割します。
-
クラスターされていないタイマーにこのデータベースデータストアを使用するには、refresh-interval をゼロに設定し、すべてのノードが一意のパーティション名を持つか、各ノードに異なるデータベースを使用していることを確認します。
8.12.2. デプロイメントでの Jakarta Enterprise Beans でクラスター化されたタイマーの使用
単一のデータストアをすべてのアプリケーションのデフォルトとして使用するか、各アプリケーションに特定のデータストアを使用できます。
要件
- Jakarta Enterprise Beans でクラスター化したデータベースベースのタイマーストアを設定している。
手順
単一のデータストアをすべてのアプリケーションにデフォルトとして使用するには、以下のように
ejb3サブシステム内でdefault-data-storeを更新します。<timer-service thread-pool-name="timer" default-data-store="clustered-store"> <data-stores> <database-data-store name="clustered-store" datasource-jndi-name="java:jboss/datasources/ExampleDS" partition="timer"/> </data-stores> </timer-service>特定のアプリケーションに別のデータストアを使用するには、タイマーデータストア名を
jboss-ejb3.xmlファイルに設定します。<?xml version="1.1" encoding="UTF-8"?> <jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:timer="urn:timer-service:1.0" xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd" version="3.1" impl-version="2.0"> <assembly-descriptor> <timer:timer> <ejb-name>*</ejb-name> <timer:persistence-store-name>my-clustered-store</timer:persistence-store-name> </timer:timer> </assembly-descriptor> </jboss:ejb-jar>
8.12.3. Jakarta インターセプターを使用した Jakarta Enterprise Beans でクラスタータイマーの更新
タイマーをプログラムで更新するには、ビジネスメソッドで Jakarta インターセプターを設定し、refresh-interval が期限切れになる前にタイマーを強制的に更新することができます。
クラスター化されたデプロイメントでは、複数のノードがデータストアを短時間で更新した場合に、インメモリータイマーの状態が同期されなくなります。
要件
- データベースでサポートされるクラスター化された Jakarta Enterprise Beans を設定していること。
手順
wildfly.ejb.timer.refresh.enabledをtrueを有効にする Jakarta インターセプターを実装します。import javax.interceptor.AroundInvoke; import javax.interceptor.Interceptor; import javax.interceptor.InvocationContext; /** * An interceptor to enable programmatic timer refresh across multiple nodes. */ @Interceptor public class RefreshInterceptor { @AroundInvoke public Object intercept(InvocationContext context) throws Exception { context.getContextData().put("wildfly.ejb.timer.refresh.enabled", Boolean.TRUE); return context.proceed(); } }Jakarta インターセプターを設定します。
Jakarta Interceptors は、ターゲットのステートレスまたはシングルトン Bean ビジネスメソッドに設定できます。
wildfly.ejb.timer.refresh.enabledがtrueに設定された場合、タイマーを返す前にTimerService.getAllTimers()を呼び出すと、タイマーデータストアが更新されます。@Singleton public class RefreshBean1 ... { @Interceptors(RefreshInterceptor.class) public void businessMethod1() { ... // since wildfly.ejb.timer.refresh.enabled is set to true in interceptor for this business method, // calling timerService.getAllTimers() will first refresh from timer datastore before returning timers. final Collection<Timer> allTimers = timerService.getAllTimers(); ... } }または、専用のビジネスメソッドを実装して、必要に応じてアプリケーションの他の部分が呼び出されるタイマーをプログラム的に更新できます。
@Interceptors(RefreshInterceptor.class) public List<Timer> getAllTimerInfoWithRefresh() { return timerService.getAllTimers(); } public void businessMethod1() { final LocalBusinessInterface businessObject = sessionContext.getBusinessObject(LocalBusinessInterface.class); businessObject.getAllTimerInfoWithRefresh(); // timer has been programmatically refreshed from datastore. // continue with other business logic... }