Groovy で ZooKeeper を組み込み実行

以前、Groovy で Apache ZooKeeper を使う にて ZooKeeper のクライアントを Groovy スクリプトで実装しましたが、今回は ZooKeeper のサーバーを Groovy で組み込み実行してみました。

サンプルソースは http://github.com/fits/try_samples/tree/master/blog/20131109/

ZooKeeper 組み込み実行スクリプト

ZooKeeper を組み込み実行するための単純なスクリプトは下記のようになります。 第一引数に ZooKeeper 設定ファイルのパスを指定して実行します。

zk_run.groovy
@Grapes([
    @Grab("org.apache.zookeeper:zookeeper:3.4.5"),
    @GrabExclude("com.sun.jmx#jmxri"),
    @GrabExclude("com.sun.jdmk#jmxtools"),
    @GrabExclude("javax.jms#jms")
])
import org.apache.zookeeper.server.quorum.QuorumPeerMain

new QuorumPeerMain().initializeAndRun(args)
// 単独実行しかしないのであれば下記でも可
// org.apache.zookeeper.server.ZooKeeperServerMain.main(args)

複製モードで実行しないのであれば、QuorumPeerMain の代わりに ZooKeeperServerMain を直接使う方法もあります。

また、ログ出力を行うには ZooKeeper の conf ディレクトリにある log4j.properties ファイルを上記スクリプトと同じディレクトリへ配置しておきます。

単独モード実行

それでは単独モードで実行してみます。

まずは、必要最小限の設定を記載した ZooKeeper の設定ファイルを用意しておきます。

u0/zoo.cfg (設定ファイル)
dataDir=u0/tmp/zookeeper
clientPort=2181

設定ファイルへのパスを引数にして zk_run.groovy を実行すれば ZooKeeper が起動します。

実行
> groovy zk_run.groovy u0/zoo.cfg

2013-11-09 10:57:13,730 [myid:] - INFO  [main:QuorumPeerConfig@101] - Reading configuration from: u0/zoo.cfg
2013-11-09 10:57:13,746 [myid:] - INFO  [main:DatadirCleanupManager@78] - autopurge.snapRetainCount set to 3
2013-11-09 10:57:13,746 [myid:] - INFO  [main:DatadirCleanupManager@79] - autopurge.purgeInterval set to 0
2013-11-09 10:57:13,746 [myid:] - INFO  [main:DatadirCleanupManager@101] - Purge task is not scheduled.
2013-11-09 10:57:13,746 [myid:] - WARN  [main:QuorumPeerMain@113] - Either no config or no quorum defined in config, running  in standalone mode
・・・

zkCli コマンドを使って接続できます。

> zkCli

Connecting to localhost:2181
・・・

複製モード実行

次に複製モードの実行を試してみます。

複製モード用の設定ファイル

まずは設定ファイルを用意します。 単独実行と比べて initLimit、syncLimit、server 設定の追加が必要です。

server 設定は下記のようなフォーマットとなっており、ポート番号を 2つ指定する必要があります。 (サーバー同士の接続ポートとリーダー選出に使用するポートのようです)

server.<サーバー番号>=<サーバー名>:<ポート番号1>:<ポート番号2>

今回は同一ホスト上で実行するのでポート番号をそれぞれ分けています。

なお、ZooKeeper の複製モードは最低 3サーバー構成で実行するようです。 2サーバー構成で実行すると WARN ログ No server failure will be tolerated. You need at least 3 servers. が出力されます。

u1/zoo.cfg (設定ファイル1)
dataDir=u1/tmp/zookeeper
clientPort=2181

initLimit=10
syncLimit=5

server.1=localhost:2888:3888
server.2=localhost:2889:3889
server.3=localhost:2890:3890
u2/zoo.cfg (設定ファイル2)
dataDir=u2/tmp/zookeeper
clientPort=2182

initLimit=10
syncLimit=5

server.1=localhost:2888:3888
server.2=localhost:2889:3889
server.3=localhost:2890:3890
u3/zoo.cfg (設定ファイル3)
dataDir=u3/tmp/zookeeper
clientPort=2183

initLimit=10
syncLimit=5

server.1=localhost:2888:3888
server.2=localhost:2889:3889
server.3=localhost:2890:3890

myid ファイル

更に、複製モードではサーバー番号を設定した myid ファイルを予め配置しておく必要があります。

myid ファイルは設定ファイルの dataDir で指定したディレクトリへ配置します。

u1/tmp/zookeeper/myid
1
u2/tmp/zookeeper/myid
2
u3/tmp/zookeeper/myid
3

実行

サーバーを順次実行します。

他のサーバーを起動していない状態では ConnectException の WARN ログが出力されますが、全サーバーを起動すると出なくなります。

サーバー1 実行
> groovy zk_run.groovy u1/zoo.cfg

2013-11-09 20:53:09,157 [myid:] - INFO  [main:QuorumPeerConfig@101] - Reading configuration from: u1/zoo.cfg
・・・
2013-11-09 20:53:10,390 [myid:1] - WARN  [WorkerSender[myid=1]:QuorumCnxManager@368] - Cannot open channel to 2 at election address localhost/127.0.0.1:3889
java.net.ConnectException: Connection refused: connect
        at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
        ・・・
・・・
2013-11-09 21:02:04,636 [myid:1] - INFO  [WorkerReceiver[myid=1]:FastLeaderElection@542] - Notification: 2 (n.leader), 0x0 (n.zxid), 0x1 (n.round), LOOKING (n.state), 3 (n.sid), 0x3 (n.peerEPoch), FOLLOWING (my state)
サーバー2 実行
> groovy zk_run.groovy u2/zoo.cfg

・・・
2013-11-09 21:02:04,948 [myid:2] - INFO  [LearnerHandler-/127.0.0.1:49289:LearnerHandler@419] - Sending snapshot last zxid of peer is 0x0  zxid of leader is 0x400000000sent zxid of db as 0x400000000
サーバー3 実行
> groovy zk_run.groovy u3/zoo.cfg

・・・
2013-11-09 21:02:04,948 [myid:3] - INFO  [QuorumPeer[myid=3]/0:0:0:0:0:0:0:0:2183:FileTxnSnapLog@240] - Snapshotting: 0x400000000 to u3/tmp/zookeeper/version-2/snapshot.400000000