1. 概要

ほとんどの分散アプリケーションでは、一貫性がありフォールトトレラントであるために、いくつかのステートフルコンポーネントが必要です。 Atomixは、分散リソースのフォールトトレランスと一貫性の実現に役立つ組み込み可能なライブラリです。

コレクション、グループ、同時実行用ツールなどのリソースを管理するための豊富なAPIセットを提供します。

開始するには、次のMaven依存関係をpomに追加する必要があります。

<dependency>
    <groupId>io.atomix</groupId>
    <artifactId>atomix-all</artifactId>
    <version>1.0.8</version>
</dependency>

この依存関係は、ノードが相互に通信するために必要なNettyベースのトランスポートを提供します。

2. クラスターのブートストラップ

Atomixの使用を開始するには、最初にクラスターをブートストラップする必要があります。

Atomixは、ステートフルな分散リソースを作成するために使用される一連のレプリカで構成されています。 各レプリカは、クラスターに存在する各リソースの状態のコピーを維持します。

レプリカは、クラスター内のアクティブとパッシブの2つのタイプです。

分散リソースの状態変化はアクティブレプリカを介して伝播されますが、パッシブレプリカはフォールトトレランスを維持するために同期が保たれます。

2.1. 組み込みクラスターのブートストラップ

単一ノードクラスターをブートストラップするには、最初にAtomixReplicaのインスタンスを作成する必要があります。

AtomixReplica replica = AtomixReplica.builder(
  new Address("localhost", 8700))
   .withStorage(storage)
   .withTransport(new NettyTransport())
   .build();

ここで、レプリカはストレージおよびトランスポートで構成されています。 ストレージを宣言するコードスニペット:

Storage storage = Storage.builder()
  .withDirectory(new File("logs"))
  .withStorageLevel(StorageLevel.DISK)
  .build();

レプリカが宣言され、ストレージとトランスポートで構成されたら、 bootstrap()を呼び出すだけでレプリカをブートストラップできます。これにより、サーバーがブートストラップされるまでブロックに使用できるCompleteableFutureが返されます。関連するブロッキングjoin()メソッドを呼び出すことによって:

CompletableFuture<AtomixReplica> future = replica.bootstrap();
future.join();

これまで、単一ノードクラスターを構築してきました。 これで、ノードを追加できます。

これを行うには、他のレプリカを作成し、それらを既存のクラスターに結合する必要があります。 join(Address)メソッドを呼び出すための新しいスレッドを生成する必要があります。

AtomixReplica replica2 = AtomixReplica.builder(
  new Address("localhost", 8701))
    .withStorage(storage)
    .withTransport(new NettyTransport())
    .build();
  
replica2
  .join(new Address("localhost", 8700))
  .join();

AtomixReplica replica3 = AtomixReplica.builder(
  new Address("localhost", 8702))
    .withStorage(storage)
    .withTransport(new NettyTransport())
    .build();

replica3.join(
  new Address("localhost", 8700), 
  new Address("localhost", 8701))
  .join();

これで、3つのノードがクラスターブートストラップされました。 または、を渡すことでクラスターをブートストラップすることもできますリストのアドレスの bootstrap(リスト

)。

方法:

List<Address> cluster = Arrays.asList(
  new Address("localhost", 8700), 
  new Address("localhost", 8701), 
  new Address("localhsot", 8702));

AtomixReplica replica1 = AtomixReplica
  .builder(cluster.get(0))
  .build();
replica1.bootstrap(cluster).join();

AtomixReplica replica2 = AtomixReplica
  .builder(cluster.get(1))
  .build();
            
replica2.bootstrap(cluster).join();

AtomixReplica replica3 = AtomixReplica
  .builder(cluster.get(2))
  .build();

replica3.bootstrap(cluster).join();

レプリカごとに新しいスレッドを生成する必要があります。

2.2. スタンドアロンクラスターのブートストラップ

Atomixサーバーは、MavenCentralからダウンロードできるスタンドアロンサーバーとして実行できます。 簡単に言えば、これはJavaアーカイブであり、以下を提供することで端末を介して実行できます。

簡単に言えば、これはJavaアーカイブであり、アドレスフラグに host:port パラメータを指定し、 -bootstrap フラグを使用することで、端末を介して実行できます。

クラスタをブートストラップするコマンドは次のとおりです。

java -jar atomix-standalone-server.jar 
  -address 127.0.0.1:8700 -bootstrap -config atomix.properties

ここで、 atomix.properties は、ストレージとトランスポートを構成するための構成ファイルです。 マルチノードクラスターを作成するには、-joinフラグを使用して既存のクラスターにノードを追加できます。

その形式は次のとおりです。

java -jar atomix-standalone-server.jar 
  -address 127.0.0.1:8701 -join 127.0.0.1:8700

3. クライアントとの連携

Atomixは、 AtomixClient APIを介して、クラスターにリモートアクセスできるクライアントの作成をサポートしています。

クライアントはステートフルである必要がないため、AtomixClientにはストレージがありません。 トランスポートはクラスターとの通信に使用されるため、クライアントの作成中にトランスポートを構成する必要があります。

トランスポートを使用してクライアントを作成しましょう。

AtomixClient client = AtomixClient.builder()
  .withTransport(new NettyTransport())
  .build();

次に、クライアントをクラスターに接続する必要があります。

リストアドレスを宣言し、リストを引数としてクライアントの connect()メソッドに渡すことができます。

client.connect(cluster)
  .thenRun(() -> {
      System.out.println("Client is connected to the cluster!");
  });

4. 取り扱いリソース

Atomixの真の力は、分散リソースを作成および管理するための強力なAPIセットにあります。 リソースはクラスター内で複製および永続化され、複製された状態マシンによって強化されます。これは、Raftコンセンサスプロトコルの基盤となる実装によって管理されます。

分散リソースは、その get()メソッドの1つによって作成および管理できます。 AtomixReplicaから分散リソースインスタンスを作成できます。

replicaAtomixReplicaのインスタンスであると考えると、分散マップリソースを作成し、それに値を設定するためのコードスニペットは次のようになります。

replica.getMap("map")
  .thenCompose(m -> m.put("bar", "Hello world!"))
  .thenRun(() -> System.out.println("Value is set in Distributed Map"))
  .join();

ここで、 join()メソッドは、リソースが作成されて値が設定されるまでプログラムをブロックします。 AtomixClient を使用して同じオブジェクトを取得し、 get( “bar”)メソッドを使用して値を取得できます。

最後にget()メソッドを使用して、結果を待つことができます。

String value = client.getMap("map"))
  .thenCompose(m -> m.get("bar"))
  .thenApply(a -> (String) a)
  .get();

5. 一貫性とフォールトトレランス

Atomixは、可用性よりも一貫性がはるかに重要なミッションクリティカルな小規模データセットに使用されます。

読み取りと書き込みの両方の線形化可能性を通じて、構成可能な強力な一貫性を提供します。 線形化可能性では、書き込みがコミットされると、すべてのクライアントが結果の状態を認識することが保証されます。

Atomixのクラスターの一貫性は、選出されたリーダーが以前に成功したすべての書き込みを持つ、基盤となるRaftコンセンサスアルゴリズムによって保証されます。

すべての新しい書き込みはクラスターリーダーを通過し、完了する前にサーバーの大部分に同期的に複製されます。

フォールトトレランスを維持するには、クラスターのマジョリティサーバーが稼働している必要があります。 少数のノードに障害が発生した場合、ノードは非アクティブとしてマークされ、パッシブノードまたはスタンバイノードに置き換えられます。

リーダーに障害が発生した場合、クラスター内の残りのサーバーが新しいリーダーの選出を開始します。 その間、クラスターは使用できなくなります。

パーティションの場合、リーダーがパーティションの非クォーラム側にある場合、リーダーはステップダウンし、クォーラムのある側で新しいリーダーが選出されます。

そして、リーダーが過半数を占める場合、それは変わらずに継続します。 パーティションが解決されると、非クォーラム側のノードがクォーラムに参加し、それに応じてログを更新します。

6. 結論

ZooKeeperと同様に、Atomixは、分散コンピューティングの問題を処理するための堅牢なライブラリセットを提供します。

そして、いつものように、このタスクの完全なソースコードはGitHub利用できます。