Apacheキュレーター入門

1前書き

Apache Curator はhttps://zookeeper.apache.org/[Apache Zookeeper]、分散アプリケーション用の一般的な調整サービスのためのJavaクライアントです。

このチュートリアルでは、Curatorが提供する最も関連性の高い機能をいくつか紹介します。

  • 接続管理 - 接続の管理とポリシーの再試行

  • 非同期 - 非同期機能と

Java 8ラムダの使用 ** 設定管理 - のための集中設定

システム ** 強く型付けされたモデル - 型付けされたモデルを扱う

  • レシピ - リーダー選挙、分散錠、カウンター

2前提条件

まずはじめに、https://zookeeper.apache.org/[Apache Zookeeper]とその機能を簡単に見てみることをお勧めします。

このチュートリアルでは、 127.0.0.1:2181 でスタンドアロンZookeeperインスタンスがすでに実行されていると仮定します。インストールを開始したばかりの場合は、https://zookeeper.apache.org/doc/current/zookeeperStarted.html[インストール方法と実行方法]の説明があります。

まず、https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.apache.curator%22%20AND%20a%3A%22curator-x-を追加する必要がありますasync%22[curator-x-async]私たちの pom.xml への依存関係:

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-x-async</artifactId>
    <version>4.0.1</version>
    <exclusions>
        <exclusion>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </exclusion>
    </exclusions>
</dependency>
  • 最新版のApache Curator 4.X.XはZookeeper 3.5.X ** と強く依存しています。現在もまだベータ版です。

そのため、この記事では、代わりに現在最新の安定版https://zookeeper.apache.org/doc/r3.4.11/index.html[Zookeeper 3.4.11]を使用します。

そのため、Zookeeperの依存関係を除外してhttps://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.apache.zookeeper%22%20AND%20a%3A%22zookeeper%22を追加する必要があります[私たちのZookeeperバージョンへの依存性]私たちの pom.xml へ:

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.11</version>
</dependency>

互換性の詳細については、https://curator.apache.org/zk-compatibility.html[このリンク]を参照してください。

3接続管理

  • Apache Curatorの基本的な使用例は、実行中のApache Zookeeperインスタンスに接続することです。

このツールは、再試行ポリシーを使用してZookeeperへの接続を構築するためのファクトリを提供します。

int sleepMsBetweenRetries = 100;
int maxRetries = 3;
RetryPolicy retryPolicy = new RetryNTimes(
  maxRetries, sleepMsBetweenRetries);

CuratorFramework client = CuratorFrameworkFactory
  .newClient("127.0.0.1:2181", retryPolicy);
client.start();

assertThat(client.checkExists().forPath("/")).isNotNull();

この簡単な例では、3回再試行し、接続に問題がある場合は再試行間に100ミリ秒待機します。

CuratorFramework クライアントを使用してZookeeperに接続したら、パスをブラウズしたり、データを取得/設定したり、基本的にサーバーと対話したりできます。

4非同期

  • Curator Asyncモジュールは、https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionStage.htmlを使用して、上記の CuratorFramework クライアントをブロックしない機能を提供します。 API]。

前の例でAsyncラッパーを使用した場合の様子を見てみましょう。

int sleepMsBetweenRetries = 100;
int maxRetries = 3;
RetryPolicy retryPolicy
  = new RetryNTimes(maxRetries, sleepMsBetweenRetries);

CuratorFramework client = CuratorFrameworkFactory
  .newClient("127.0.0.1:2181", retryPolicy);

client.start();
AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client);

AtomicBoolean exists = new AtomicBoolean(false);

async.checkExists()
  .forPath("/")
  .thenAcceptAsync(s -> exists.set(s != null));

await().until(() -> assertThat(exists.get()).isTrue());

現在、 checkExists() 操作は非同期モードで動作し、メインスレッドをブロックしません。 CompletionStageを使用する代わりに thenAcceptAsync() メソッドを使用して、アクションを順番にチェーン化することもできますAPI

5構成管理

分散環境で最も一般的な課題の1つは、多くのアプリケーション間で共有構成を管理することです。設定を保存するデータストアとしてZookeeperを使用できます。

Apache Curatorを使ってデータを取得および設定する例を見てみましょう。

CuratorFramework client = newClient();
client.start();
AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client);
String key = getKey();
String expected = "my__value";

client.create().forPath(key);

async.setData()
  .forPath(key, expected.getBytes());

AtomicBoolean isEquals = new AtomicBoolean();
async.getData()
  .forPath(key)
  .thenAccept(data -> isEquals.set(new String(data).equals(expected)));

await().until(() -> assertThat(isEquals.get()).isTrue());

この例では、ノードパスを作成し、Zookeeperにデータを設定してから、値が同じであることを確認しながら復元します。 key フィールドは /config/dev/my key__のようなノードパスです。

5.1. ウォッチャー

Zookeeperのもう1つの興味深い機能は、キーまたはノードを監視する機能です。 再デプロイすることなく 設定の変更を監視してアプリケーションを更新することができます。

ウォッチャーを使用した場合の上記の例の外観を見てみましょう。

CuratorFramework client = newClient()
client.start();
AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client);
String key = getKey();
String expected = "my__value";

async.create().forPath(key);

List<String> changes = new ArrayList<>();

async.watched()
  .getData()
  .forPath(key)
  .event()
  .thenAccept(watchedEvent -> {
    try {
        changes.add(new String(client.getData()
          .forPath(watchedEvent.getPath())));
    } catch (Exception e) {
       //fail ...
    }});
//Set data value for our key
async.setData()
  .forPath(key, expected.getBytes());

await()
  .until(() -> assertThat(changes.size()).isEqualTo(1));

ウォッチャーを構成し、データを設定してから、監視イベントがトリガーされたことを確認します。一度に1つのノードまたは一連のノードを監視できます。

6. 強く型付けされたモデル

Zookeeperは主にバイト配列で動作するため、データをシリアル化し、逆シリアル化する必要があります。これにより、シリアライズ可能なインスタンスでも柔軟に作業できるようになりますが、保守が難しい場合があります。

これを手助けするために、Curatorはhttps://curator.apache.org/curator-x-async/modeled.html[typed models]の概念を追加します。それがどのように機能するのか見てみましょう。

まず、シリアライザフレームワークが必要です。キュレーターはJacksonの実装を使用することをお勧めしますので、https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22com.fasterxml.jackson.core%22%20AND%20a%3A%22jacksonを追加しましょう。 -databind%22[Jacksonの依存関係]を pom.xml に追加します。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.4</version>
</dependency>

それでは、カスタムクラス HostConfig を永続化してみましょう。

public class HostConfig {
    private String hostname;
    private int port;

   //getters and setters
}

HostConfig クラスからパスへのモデル仕様マッピングを提供し、Apache Curatorによって提供されるモデル化フレームワークラッパーを使用する必要があります。

ModelSpec<HostConfig> mySpec = ModelSpec.builder(
  ZPath.parseWithIds("/config/dev"),
  JacksonModelSerializer.build(HostConfig.class))
  .build();

CuratorFramework client = newClient();
client.start();

AsyncCuratorFramework async
  = AsyncCuratorFramework.wrap(client);
ModeledFramework<HostConfig> modeledClient
  = ModeledFramework.wrap(async, mySpec);

modeledClient.set(new HostConfig("host-name", 8080));

modeledClient.read()
  .whenComplete((value, e) -> {
     if (e != null) {
          fail("Cannot read host config", e);
     } else {
          assertThat(value).isNotNull();
          assertThat(value.getHostname()).isEqualTo("host-name");
          assertThat(value.getPort()).isEqualTo(8080);
     }
   });

パス /config/dev を読み込むときの whenComplete() メソッドは、Zookeeperの HostConfig インスタンスを返します。

7. レシピ

Zookeeperは、リーダー選挙、分散ロック、共有カウンターなどの高度なソリューションやレシピを実装するためのhttps://zookeeper.apache.org/doc/current/recipes.html[このガイドライン]を提供しています。

Apache Curatorはこれらのレシピのほとんどに実装を提供します。完全なリストを見るには、https://curator.apache.org/curator-recipes/index.html[キュレーターレシピのドキュメント]をご覧ください。

これらのレシピはすべて別々のモジュールで入手できます。

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>4.0.1</version>
</dependency>

いくつかの簡単な例を見て、すぐに理解し始めましょう。

7.1. リーダー選挙

分散環境では、複雑なジョブを調整するために1つのマスターノードまたはリーダーノードが必要になる場合があります。

これが、キュレーターでのhttps://curator.apache.org/curator-recipes/leader-election.html[リーダー選挙レシピ]の使用方法は次のようになります。

CuratorFramework client = newClient();
client.start();
LeaderSelector leaderSelector = new LeaderSelector(client,
  "/mutex/select/leader/for/job/A",
  new LeaderSelectorListener() {
      @Override
      public void stateChanged(
        CuratorFramework client,
        ConnectionState newState) {
      }

      @Override
      public void takeLeadership(
        CuratorFramework client) throws Exception {
      }
  });
//join the members group
leaderSelector.start();
//wait until the job A is done among all members
leaderSelector.close();

リーダーセレクタを起動すると、ノードはパス /mutex/select/leader/for/job/A 内のメンバーグループに参加します。ノードがリーダーになると、 takeLeadership メソッドが呼び出され、リーダーとして私たちは仕事を再開できます。

7.2. 共有ロック

The Shared Lock recipe は完全に分散されたロックを持つことについてです。

CuratorFramework client = newClient();
client.start();
InterProcessSemaphoreMutex sharedLock = new InterProcessSemaphoreMutex(
  client, "/mutex/process/A");

sharedLock.acquire();
//do process A

sharedLock.release();

ロックを取得すると、Zookeeperは他のアプリケーションが同じロックを同時に取得しないようにします。

7.3. カウンター

The Counters recipe はすべてのクライアント間で共有される Integer を調整します。

CuratorFramework client = newClient();
client.start();

SharedCount counter = new SharedCount(client, "/counters/A", 0);
counter.start();

counter.setCount(counter.getCount() + 1);

assertThat(counter.getCount()).isEqualTo(1);

この例では、Zookeeperは Integer 値をパス /counters/A に格納し、パスがまだ作成されていない場合は値を 0 に初期化します。

8結論

この記事では、Apache Curatorを使ってApache Zookeeperに接続し、その主な機能を利用する方法を説明しました。

また、Curatorの主なレシピをいくつか紹介しました。

いつものように、ソースはhttps://github.com/eugenp/tutorials/tree/master/apache-curator[over on GitHub]にあります。