ヒマをみつけてWeb開発
その場の思い付きを、ヒマをみつけてWebサイトにしてみるブログ

Zookeeperメモ

Sunday, 16 May 2010 18:17 by sabro

Zookeeperが、けっこう独自ルール多いので、忘れないようにメモ。英語のドキュメント読んで理解してるから、間違ってる可能性あり。ちなみに、.NETドライバでの場合です。

  • Zookeeperクラスのインスタンスは、何度も使いまわすことが可能
  • Zookeeperクラスのコンストラクタに渡す、セッションタイムアウト値は、あくまでも一回の通信におけるタイムアウト値であって、Zookeeperインスタンスをタイムアウト値以上の時間保持しても、インスタンスが使えなくなることはない
  • Zookeeperクラスのコンストラクタに渡す、セッションタイムアウト値は、設定ファイルのtickTime値の2倍以上、20倍以下でないといけない
  • Zookeeperクラスのコンストラクタに渡すWatcherはデフォルトのウォッチャーであり、接続状態が変わる度にイベントが起こる、Zookeeperインスタンス生成時は、Connectionが非接続から接続に変わるので、いきなりイベント発生するので注意
  • Zookeeper.Exists、Zookeeper.GetData、Zookeeper.GetChildrenに渡すWatcherは観測対象のノードに変化があったときにイベント発生、イベントは1回しか起きない
  • 接続文字列には、複数のホストをカンマ区切りで指定可能、その場合は再接続のたびに適当に接続先を選んでくれる、またホストに接続出来なかった場合、別のホストへつないでくれる
  • クライアントアプリから接続後、クラスタを再起動しても、Zookeeperインスタンスを作りなおすことなく再接続できた、うまく再接続する仕組みになってるっぽい
  • Zookeeper.Deleteメソッドに渡すデータのバージョンは、-1を渡してやると、どんなバージョンでも削除してくれる、Zookeeper.SetDataのバージョンも同様
  • CreateModeで、PersistentSequentialや、EphemeralSequentialを指定すると、Node名の末尾に連番をつけてくれるが、これはNode名ごとではなく、ツリー全体で1つの連番を使う、"node1","node2"と作ったあとに"tmp3"を作ると、次は"node4"になる、普通アプリには複数の連番IDが必要なので、この機能で連番を作るのは厳しい

注意点も多いけど、再接続とかを、うまいことやってくれるのは、いい感じですね( ̄∇  ̄ )

Tags:   ,
Categories:   NoSQL
Actions:   Permalink | Comments (44) | Comment RSSRSS comment feed

Zookeeperを.NETから使ってみる

Thursday, 13 May 2010 15:11 by sabro

Cassandraemonの正式版はもう少しお待ちを。実際にうちのサービスに組み込んでみて、安定して動くことを確認してからリリースします。

で、今組み込み作業中なんですが、CassandraだとMySQLでやってた連番生成が出来ないことに気づきました。Diggは、ZooKeeperを使ってやってるみたいです。TwitterはMySQLなのかな。

Zookeeperは、分散システムを協調動作させるために、Hadoopチームによって作られたライブラリです。データ構造は、ファイルシステムのようなツリー構造になっていて、それぞれのノードに対して、アトミックで順序保証されたアクセスが可能とのこと。

でも、Zookeeperは今のところ正式なものとしては、Java、C言語しかインターフェースが用意されていません。一応Http Restでの呼び出しでZookeeperにアクセスできる実験的機能があるみたいですが、これはWatcherがJavascriptからしか使えないとか制限があるみたいですね。

これは諦めるしかないかなと思ってたんですが、調べてみると本家ZookeeperをForkして、.NETドライバを作ってる方がいました!

http://github.com/ewhauser/zookeeper

最終更新日が5月4日。まだ作ってる最中かもしれないですね。本家にマージされたりしないのかな( ̄∇  ̄ )

Gitでソース取ってきて、MonoDevelopで開いたところ、いくつかソースが足りません。どうやら、Antでソースを作る必要があるみたいなので、トップディレクトリでAnt実行。すると、無事ソースコードが作られてビルド成功しました(^^)

それでは、せっかくなんでC#で接続してみます。Zookeeperのインスタンスを作るには、IWatcherインターフェースを実装したクラスを自作しないといけないみたいなんで、適当にでっちあげます。(追記:Watcherが不要なら、Nullを渡してもいいみたいです)

public class DefaultWatcher : IWatcher
{
	public void Process (WatchedEvent @event)
	{
		Console.WriteLine(@event.Path);
		Console.WriteLine(@event.Type);
		Console.WriteLine(@event.State);
	}
}

最初は、ZNodeを作る例です。Zookeeperのコンストラクタには、接続ホスト名&ポート、セッションタイムアウト、でっちあげたWatcherを渡します。Createメソッドでは、ノードパス、格納データ、接続ACL、CreateModeを渡してます。接続ACLはいくつか定数で用意されてたので、そのまま使いました( ̄∇  ̄ )

using(var zookeeper = new ZooKeeper("RemoteLinuxServer:2181", 
                                    new TimeSpan(0,1,0), 
                                    new DefaultWatcher()))
{
	var response = zookeeper.Create("/node1", 
	                                Encoding.UTF8.GetBytes("data1"), 
	                                Ids.OPEN_ACL_UNSAFE,
	                                CreateMode.Persistent);
	                                

	Console.WriteLine("Create : " + response);
}

// ### result ###
// Create : /node1

接続先のLinuxサーバ側で確認してみると、ルートディレクトリに「node1」が作られていました。今度は、Linuxサーバ側で、node2を作ってみます。

[root@RemoteLinuxServer bin]# ./zkCli.sh -server 127.0.0.1:2181
[zk: 127.0.0.1:2181(CONNECTED) 1] ls /
[node1, zookeeper]
[zk: 127.0.0.1:2181(CONNECTED) 2] create /node2 data2
Created /node2

再び、C#に戻って、GetChildrenメソッドにパスと、Watchフラグを渡して実行。Linuxサーバ側で追加したnode2が取れてます。

using(var zookeeper = new ZooKeeper("RemoteLinuxServer:2181", 
                                    new TimeSpan(0,1,0), 
                                    new DefaultWatcher()))
{
	var responses = zookeeper.GetChildren("/", false);

	foreach(var res in responses)
	{
		Console.WriteLine("GetChildren : " + res);
	}
}

// ### result ###
// GetChildren : node2
// GetChildren : node1
// GetChildren : zookeeper

うーん、グッジョブと言わざるをえない。とりあえず、もうちょっといじってみたいと思います( ̄∇  ̄ )

Tags:   ,
Categories:   .NET | NoSQL
Actions:   Permalink | Comments (46) | Comment RSSRSS comment feed

DateTime.Ticksの精度は100ナノ秒ではない

Thursday, 6 May 2010 07:41 by sabro

DateTime.Ticksプロパティの説明には

「このプロパティの値は、0001 年 1 月 1 日午前 00:00:00 (DateTime.MinValue を表します) 以降の経過時間 (100 ナノ秒単位) を表します。」

って書いてあります。でも、これは桁数がそれだけ用意してありますよっていうだけで、実際は、動作OSによって精度が異なるみたいです。

ちょっとうちのVistaで実験

static void Main(string[] args)
{
	for (int i = 0; i < 100; i++)
	{
		Console.WriteLine(DateTime.Now.Ticks);
	}
}

こんなかんじで実行したところ、結果は以下のような感じでした。

634086940404282076
634086940404312076
634086940404312076
634086940404312076
634086940404312076
634086940404312076
634086940404312076
634086940404312076
634086940404312076
634086940404312076
634086940404312076
634086940404312076
634086940404312076
634086940404312076
634086940404312076
634086940404312076
634086940404322076
634086940404322076
634086940404322076
634086940404322076
634086940404322076
634086940404322076
634086940404322076
634086940404322076
634086940404322076
:
:

下4桁は固定で、1ミリ秒単位で増加するようです。うっかりMSDNの内容をうのみにしないように気をつけないといけませんね。

こっから蛇足。

なんで、こんな実験したかっていうと、作成中のCassandra LINQプロバイダ、Cassandraemonで、TimeUUIDをキーにする必要があったからなんです。TimeUUIDは、現在の100ナノ秒単位のタイムスタンプから生成されるIDのようなもので、かなりの確率で複数マシン間で一意になることになってます。

ところが、これをDateTime.Ticksから生成すると、1ミリ秒単位なので結構な確率で同じものが生成されてしまうんですね。これでは、テーブルのキーとして使うには厳しいわけです。

うーん、内部でミリ秒以下は適当に生成するとかで凌ぐしかないのかなあ。なんかいいアイデアないでしょうか( ̄∇  ̄ )

Tags:   , , , ,
Categories:   .NET
Actions:   Permalink | Comments (43) | Comment RSSRSS comment feed