• 検索結果がありません。

HA シングルトンサービス

第 6 章 WEB アプリケーションのクラスター化

6.4. HA シングルトンサービス

クラスター化されたシングルトンサービス (高可用性 (HA) シングルトンとも呼ばれます) は、クラス ターの複数のノードにデプロイされるサービスです。このサービスはいずれかのノードでのみ提供され ます。シングルトンサービスが実行されているノードは、通常マスターマスターノードと呼ばれます。

マスター

マスターノードが失敗またはシャットダウンした場合に、残りのノードから別のマスターが選択され、

新しいマスターでサービスが再開されます。マスターが停止し、別のマスターが引き継ぐまでの短い間 を除き、サービスは 1 つのノードのみによって提供されます。

HA

シングルトン シングルトン

ServiceBuilder API

JBoss EAP 7 では、プロセスを大幅に簡略化するシングルトンサービスを構築するための新しいパブ

リック API が導入されました。

SingletonServiceConfigurator 実装により、サービスは非同期的に開始されるようインストールさ れ、Modular Service Container (MSC) のデッドロックが回避されます。

HA

シングルトンサービス選択ポリシー シングルトンサービス選択ポリシー

HA シングルトンを起動するノードの優先順位がある場合は、ServiceActivator クラスで選択ポリシー を設定できます。

JBoss EAP は、以下の 2 つの選択ポリシーを提供します。

単純な選択ポリシー

単純な選択ポリシーでは、相対的な経過時間に基づいてマスターノードが選択されます。必要 な経過時間は、利用可能なノードのリストのインデックスである position プロパティーで、以 下のように設定されます。

position = 0: 最も古いノードを参照します。これはデフォルトです。

position = 1: 2 番目に古いノードを参照します。

position を負の値にして最も新しいノードを示すこともできます。

position = -1: 最も新しいノードを参照します。

position = -2: 2 番目に新しいノードを参照します。

ランダムな選択ポリシー

ランダムな選択ポリシーでは、シングルトンサービスのプロバイダーとなるランダムなメン バーが選択されます。

HA

シングルトンサービスの優先度 シングルトンサービスの優先度

HA シングルトンサービスの選択ポリシーは、任意で 1 つ以上の優先されるサーバーを指定することが できます。優先されるサーバーが利用できる場合は、そのポリシーにおけるすべてのシングルトンアプ リケーションでそのサーバーがマスターになります。

優先度は、ノード名またはアウトバウンドソケットバインディング名を介して定義することができま す。

注記 注記

ノードの優先度は常に選択ポリシーの結果よりも優先されます。

デフォルトでは、JBoss EAP の高可用性設定によって、優先されるサーバーがない default という名前

デフォルトでは、JBoss EAP の高可用性設定によって、優先されるサーバーがない default という名前 の簡単な選択ポリシーが提供されます。優先度を設定するには、カスタムポリシーを作成し、優先され るサーバーを定義します。

クォーラム クォーラム

ネットワークパーティションがある場合、シングルトンサービスに潜在的な問題が存在します。この問 題はスプリットブレインと呼ばれ、ノードの各サブセットは他のサブセットと通信できません。サー バーの各セットは他のセットのすべてのサーバーに障害が発生したと見なし、唯一障害が発生していな いクラスターとして動作します。これによってデータの整合性に問題が発生する可能性があります。

JBoss EAP では、選択ポリシーでクォーラムを指定してスプリットブレインを防ぐことができます。

クォーラムは、シングルトンプロバイダーの選択が行われる前に存在するノードの最小数を指定しま す。

典型的なデプロイメントでは、N/2 + 1 をクォーラムとして使用します。N は予想されるクラスターサイ ズになります。この値は実行時に更新でき、アクティブなすべてのシングルトンサービスに即時反映さ れます。

HA

シングルトンサービス選択リスナー シングルトンサービス選択リスナー

新しいプライマリーシングルトンサービスプロバイダーを選択すると、登録されたすべての

SingletonElectionListener がトリガーされ、新しいプライマリープロバイダーに関するクラスターの すべてのメンバーに通知します。以下は、SingletonElectionListener の使用例です。

HA

シングルトンサービスアプリケーションの作成 シングルトンサービスアプリケーションの作成

以下に、アプリケーションを作成し、クラスター全体のシングルトンサービスとしてデプロイするのに public class MySingletonElectionListener implements SingletonElectionListener {

@Override

public void elected(List<Node> candidates, Node primary) { // ...

} }

public class MyServiceActivator implements ServiceActivator { @Override

public void activate(ServiceActivatorContext context) { String containerName = "foo";

SingletonElectionPolicy policy = new MySingletonElectionPolicy();

SingletonElectionListener listener = new MySingletonElectionListener();

int quorum = 3;

ServiceName name = ServiceName.parse("my.service.name");

// Use a SingletonServiceConfiguratorFactory backed by default cache of "foo" container Supplier<SingletonServiceConfiguratorFactory> factory = new ActiveServiceSupplier<>

(context.getServiceTarget(),

ServiceName.parse(SingletonDefaultCacheRequirement.SINGLETON_SERVICE_CONFIGURATOR_

FACTORY.resolve(containerName).getName()));

ServiceBuilder<?> builder = factory.get().createSingletonServiceConfigurator(name) .electionListener(listener)

.electionPolicy(policy) .requireQuorum(quorum)

.build(context.getServiceTarget());

Service service = new MyService();

builder.setInstance(service).install();

} }

以下に、アプリケーションを作成し、クラスター全体のシングルトンサービスとしてデプロイするのに 必要な手順の簡単な例を示します。この例では、頻繁にシングルトンサービスをクエリーし、そのシン グルトンサービスが実行されているノードの名前を取得します。

シングルトンの挙動を確認するには、最低でも 2 つのサーバーにアプリケーションをデプロイする必要 があります。シングルトンサービスが同じノードで実行されているかまたは値がリモートで取得されて いるかは透過的です。

1. SingletonService クラスを作成します。クエリーサービスによって呼び出される getValue() メソッドは、実行されているノードに関する情報を提供します。

2. クエリーサービスを作成します。シングルトンサービスの getValue() メソッドを呼び出し、そ れが稼働しているノードの名前を取得して、サーバーログに結果を書き込みます。

class SingletonService implements Service {

private Logger LOG = Logger.getLogger(this.getClass());

private Node node;

private Supplier<Group> groupSupplier;

private Consumer<Node> nodeConsumer;

SingletonService(Supplier<Group> groupSupplier, Consumer<Node> nodeConsumer) { this.groupSupplier = groupSupplier;

this.nodeConsumer = nodeConsumer;

}

@Override

public void start(StartContext context) {

this.node = this.groupSupplier.get().getLocalMember();

this.nodeConsumer.accept(this.node);

LOG.infof("Singleton service is started on node '%s'.", this.node);

}

@Override

public void stop(StopContext context) {

LOG.infof("Singleton service is stopping on node '%s'.", this.node);

this.node = null;

} }

class QueryingService implements Service {

private Logger LOG = Logger.getLogger(this.getClass());

private ScheduledExecutorService executor;

@Override

public void start(StartContext context) throws { LOG.info("Querying service is starting.");

executor = Executors.newSingleThreadScheduledExecutor();

executor.scheduleAtFixedRate(() -> {

Supplier<Node> node = new PassiveServiceSupplier<>

(context.getController().getServiceContainer(),

3. SingletonServiceActivator クラスを実装し、シングルトンサービスとクエリーサービスの両 方を構築およびインストールします。

SingletonServiceActivator.SINGLETON_SERVICE_NAME);

if (node.get() != null) {

LOG.infof("Singleton service is running on this (%s) node.", node.get());

} else {

LOG.infof("Singleton service is not running on this node.");

}

}, 5, 5, TimeUnit.SECONDS);

}

@Override

public void stop(StopContext context) { LOG.info("Querying service is stopping.");

executor.shutdown();

} }

public class SingletonServiceActivator implements ServiceActivator {

private final Logger LOG = Logger.getLogger(SingletonServiceActivator.class);

static final ServiceName SINGLETON_SERVICE_NAME =

ServiceName.parse("org.jboss.as.quickstarts.ha.singleton.service");

private static final ServiceName QUERYING_SERVICE_NAME =

ServiceName.parse("org.jboss.as.quickstarts.ha.singleton.service.querying");

@Override

public void activate(ServiceActivatorContext serviceActivatorContext) { SingletonPolicy policy = new ActiveServiceSupplier<SingletonPolicy>(

serviceActivatorContext.getServiceRegistry(),

ServiceName.parse(SingletonDefaultRequirement.POLICY.getName())).get();

ServiceTarget target = serviceActivatorContext.getServiceTarget();

ServiceBuilder<?> builder =

policy.createSingletonServiceConfigurator(SINGLETON_SERVICE_NAME).build(target);

Consumer<Node> member = builder.provides(SINGLETON_SERVICE_NAME);

Supplier<Group> group =

builder.requires(ServiceName.parse("org.wildfly.clustering.default-group"));

builder.setInstance(new SingletonService(group, member)).install();

serviceActivatorContext.getServiceTarget()

.addService(QUERYING_SERVICE_NAME, new QueryingService()) .setInitialMode(ServiceController.Mode.ACTIVE)

.install();

serviceActivatorContext.getServiceTarget().addService(QUERYING_SERVICE_NAME).setInst ance(new QueryingService()).install();

LOG.info("Singleton and querying services activated.");

} }

4. ServiceActivator クラスの名前 (例:

org.jboss.as.quickstarts.ha.singleton.service.SingletonServiceActivator) が含まれ る、org.jboss.msc.service.ServiceActivator という名前のファイルを META-INF/services/

ディレクトリーに作成します。

完全な作業例は、JBoss EAP に同梱される ha-singleton-service クイックスタートを参照してくださ い。このクイックスタートには、バックアップサービスでインストールされるシングルトンサービスを 実証する 2 つ目の例も含まれています。バックアップサービスは、シングルトンサービスの実行用に選 択されていないすべてのノード上で実行されます。また、このクイックスタートは異なる選択ポリシー の設定方法についても実証します。