10.3. HA シングルトンを実装する
概要
次の手順は、SingletonService デコレータでラップされ、クラスター全体のシングルトンサービスとして使用されるサービスをデプロイする方法を示しています。このサービスは、クラスター内で 1 回だけ開始されるスケジュールされたタイマーをアクティブにします。
手順10.3 HA シングルトンサービスを実装する
- HA シングルトンサービスアプリケーションを作成します。以下は、シングルトンサービスとしてデプロイされる
SingletonService
デコレータでラップされたサービス
の簡単な例です。完全な例は、Red Hat JBoss Enterprise Application Platform に同梱されているcluster-ha-singleton
クイックスタートにあります。このクイックスタートには、アプリケーションをビルドおよびデプロイするためのすべての手順が含まれています。- サービスを作成します。次のリストは、サービスの例です。
package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb; import java.util.Date; import java.util.concurrent.atomic.AtomicBoolean; import javax.naming.InitialContext; import javax.naming.NamingException; import org.jboss.logging.Logger; import org.jboss.msc.service.Service; import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.StartContext; import org.jboss.msc.service.StartException; import org.jboss.msc.service.StopContext; /** * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a> */ public class HATimerService implements Service<String> { private static final Logger LOGGER = Logger.getLogger(HATimerService.class); public static final ServiceName SINGLETON_SERVICE_NAME = ServiceName.JBOSS.append("quickstart", "ha", "singleton", "timer"); /** * A flag whether the service is started. */ private final AtomicBoolean started = new AtomicBoolean(false); /** * @return the name of the server node */ public String getValue() throws IllegalStateException, IllegalArgumentException { LOGGER.infof("%s is %s at %s", HATimerService.class.getSimpleName(), (started.get() ? "started" : "not started"), System.getProperty("jboss.node.name")); return ""; } public void start(StartContext arg0) throws StartException { if (!started.compareAndSet(false, true)) { throw new StartException("The service is still started!"); } LOGGER.info("Start HASingleton timer service '" + this.getClass().getName() + "'"); final String node = System.getProperty("jboss.node.name"); try { InitialContext ic = new InitialContext(); ((Scheduler) ic.lookup("global/jboss-cluster-ha-singleton-service/SchedulerBean!org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.Scheduler")).initialize("HASingleton timer @" + node + " " + new Date()); } catch (NamingException e) { throw new StartException("Could not initialize timer", e); } } public void stop(StopContext arg0) { if (!started.compareAndSet(true, false)) { LOGGER.warn("The service '" + this.getClass().getName() + "' is not active!"); } else { LOGGER.info("Stop HASingleton timer service '" + this.getClass().getName() + "'"); try { InitialContext ic = new InitialContext(); ((Scheduler) ic.lookup("global/jboss-cluster-ha-singleton-service/SchedulerBean!org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.Scheduler")).stop(); } catch (NamingException e) { LOGGER.error("Could not stop timer", e); } } } }
- クラスター化されたシングルトンとして
サービス
をインストールするアクティベーターを作成します。次のリストは、HATimerService
をクラスター化されたシングルトンサービスとしてインストールするサービスアクティベーターの例です。package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb; import org.jboss.as.clustering.singleton.SingletonService; import org.jboss.logging.Logger; import org.jboss.msc.service.DelegatingServiceContainer; import org.jboss.msc.service.ServiceActivator; import org.jboss.msc.service.ServiceActivatorContext; import org.jboss.msc.service.ServiceController; /** * Service activator that installs the HATimerService as a clustered singleton service * during deployment. * * @author Paul Ferraro */ public class HATimerServiceActivator implements ServiceActivator { private final Logger log = Logger.getLogger(this.getClass()); @Override public void activate(ServiceActivatorContext context) { log.info("HATimerService will be installed!"); HATimerService service = new HATimerService(); SingletonService<String> singleton = new SingletonService<String>(service, HATimerService.SINGLETON_SERVICE_NAME); /* * To pass a chain of election policies to the singleton, for example, * to tell JGroups to prefer running the singleton on a node with a * particular name, uncomment the following line: */ // singleton.setElectionPolicy(new PreferredSingletonElectionPolicy(new SimpleSingletonElectionPolicy(), new NamePreference("node1/singleton"))); singleton.build(new DelegatingServiceContainer(context.getServiceTarget(), context.getServiceRegistry())) .setInitialMode(ServiceController.Mode.ACTIVE) .install() ; } }
注記上記のコード例では、クラスを使用しています。org.jboss.as.clustering.singleton.SingletonService
、これは JBoss EAP プライベート API の一部です。パブリック API は JBoss EAP リリースで利用可能になり、プライベートクラスは非推奨になりますが、これらのクラスは維持され、JBoss EAP リリースサイクルの間利用可能になります。 - ServiceActivator ファイルを作成するアプリケーションの
resources/META-INF/services/
ディレクトリーにorg.jboss.msc.service.ServiceActivator
という名前のファイルを作成します。前の手順で作成した ServiceActivator クラスの完全修飾名を含む行を追加します。org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.HATimerServiceActivator
- クラスター全体のシングルトンタイマーとして使用されるタイマーを実装するシングルトン Bean を作成します。このシングルトン Bean にはリモートインターフェイスがなく、アプリケーション内の別の EJB からローカルインターフェイスを参照してはなりません。これにより、クライアントまたは他のコンポーネントによるルックアップが防止され、SingletonService がシングルトンを完全に制御できるようになります。
- スケジューラーインターフェイスを作成する
package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb; /** * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a> */ public interface Scheduler { void initialize(String info); void stop(); }
- クラスター全体のシングルトンタイマーを実装するシングルトン Bean を作成します。
package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb; import javax.annotation.Resource; import javax.ejb.ScheduleExpression; import javax.ejb.Singleton; import javax.ejb.Timeout; import javax.ejb.Timer; import javax.ejb.TimerConfig; import javax.ejb.TimerService; import org.jboss.logging.Logger; /** * A simple example to demonstrate a implementation of a cluster-wide singleton timer. * * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a> */ @Singleton public class SchedulerBean implements Scheduler { private static Logger LOGGER = Logger.getLogger(SchedulerBean.class); @Resource private TimerService timerService; @Timeout public void scheduler(Timer timer) { LOGGER.info("HASingletonTimer: Info=" + timer.getInfo()); } @Override public void initialize(String info) { ScheduleExpression sexpr = new ScheduleExpression(); // set schedule to every 10 seconds for demonstration sexpr.hour("*").minute("*").second("0/10"); // persistent must be false because the timer is started by the HASingleton service timerService.createCalendarTimer(sexpr, new TimerConfig(info, false)); } @Override public void stop() { LOGGER.info("Stop all existing HASingleton timers"); for (Timer timer : timerService.getTimers()) { LOGGER.trace("Stop HASingleton timer: " + timer.getInfo()); timer.cancel(); } } }
- クラスターリングを有効にして各 JBoss EAP 6 インスタンスを起動します。スタンドアロンサーバーのクラスターリングを有効にするには、インスタンスごとに一意のノード名とポートオフセットを使用して、
HA
プロファイルを使用して各サーバーを起動する必要があります。- Linux の場合、次のコマンド構文を使用してサーバーを起動します。
EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=UNIQUE_NODE_NAME -Djboss.socket.binding.port-offset=PORT_OFFSET
例10.4 Linux で複数のスタンドアロンサーバーを起動する
$ EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=node1 $ EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=node2 -Djboss.socket.binding.port-offset=100
- Microsoft Windows の場合、次のコマンド構文を使用してサーバーを起動します。
EAP_HOME\bin\standalone.bat --server-config=standalone-ha.xml -Djboss.node.name=UNIQUE_NODE_NAME -Djboss.socket.binding.port-offset=PORT_OFFSET
例10.5 Microsoft Windows で複数のスタンドアロンサーバーを起動します
C:> EAP_HOME\bin\standalone.bat --server-config=standalone-ha.xml -Djboss.node.name=node1 C:> EAP_HOME\bin\standalone.bat --server-config=standalone-ha.xml -Djboss.node.name=node2 -Djboss.socket.binding.port-offset=100
注記コマンドライン引数を使用したくない場合は、サーバーインスタンスごとにstandalone-ha.xml
ファイルを設定して、個別のインターフェイスにバインドできます。 - アプリケーションをサーバーにデプロイします次の Maven コマンドは、デフォルトのポートで実行されているスタンドアロンサーバーにアプリケーションをデプロイします。
mvn clean install jboss-as:deploy
追加のサーバーにデプロイするには、サーバー名を渡します。別のホストにある場合は、コマンドラインでホスト名とポート番号を渡します。mvn clean package jboss-as:deploy -Djboss-as.hostname=localhost -Djboss-as.port=10099
Maven の設定とデプロイの詳細については、JBoss EAP 6 に同梱されているcluster-ha-singleton
クイックスタートを参照してください。