Atomixの紹介

1概要

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

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

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

<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 Transport で設定されています。ストレージを宣言するコードスニペット:

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

レプリカがストレージとトランスポートで宣言され、設定されたら、 bootstrap() を呼び出すことでそれをブートストラップできます。これは CompletableFuture を返します。

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>) メソッドにアドレスの List を渡すことで、クラスタをブートストラップすることもできます。

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サーバーはMaven Centralからダウンロードできるスタンドアロンサーバーとして実行できます。簡単に言うと、これはターミナルを介して実行することができるJavaアーカイブです。

簡単に言うと、addressフラグに host:port パラメータを指定し、 -bootstrap フラグを使用することで端末経由で実行できるJavaアーカイブです。

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

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();

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

List Address として宣言し、 List を引数としてclientの connect() メソッドに渡すことができます。

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

4リソースの取り扱い

Atomixの真の力は、分散リソースを作成および管理するための強力なAPIセットにあります。 リソースは複製され、クラスタ内に保持され、複製されたステートマシンによって強化されます - その基礎となるRaft Consensus Protocolの実装によって管理されます。

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

replica AtomixReplica のインスタンスであると考えると、これは分散マップリソースを作成し、それに値を設定するためのコードの一部です。

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

ここで join() メソッドはリソースが作成されvalueがそれに設定されるまでprogramをブロックします。 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は分散コンピューティングの問題に対処するための堅牢なライブラリセットを提供します。

そして、いつものように、このタスクの完全なソースコードはhttps://github.com/eugenp/tutorials/tree/master/atomix[over on GitHub]にあります。