node.js + okuyama について | サイバーエージェント 公式エンジニアブログ
はじめまして

ブログDivでアプリエンジニアをしている川田です。


今回はV8エンジン上で動作するサーバサイドJavaScriptのnode.jsと
日本製の分散キーバリューストアであるokuyamaについての記事を書かせていただきます。



■ node.jsとokuyamaの主な特徴

▼ okuyama
・タグ機能 … データにタグ情報を付加することが可能で、タグに紐づいたデータの検索・取得が可能
・javaで実装されている … 100% javaで実装されているのでクロスプラットフォームで実行可能
・データの永続化をサポート … データの永続化・非永続化を選択可能で、永続化は特性を選ぶことも可能。
・構成要素はすべて冗長化可能 … okuyamaを構成※1するマスターノード、データノード共に冗長化可能。またデータノードはレプリケーション機能も有している。

など。

※1 okuyamaの構成
サイバーエージェント 公式エンジニアブログ


▼ node.js
・ Googleの開発したV8 JavaScriptエンジン上で動作する
・ イベントループと呼ばれる非同期処理アーキテクチャを採用 … マルチスレッドによる非同期処理ではなく、イベントが発生したタイミングでコールバック関数を呼び出すことで非同期処理を実現。(イベント駆動型)

など。



■ node.js と okuyamaの使い方

今回は、node.jsでokuyamaにデータ登録・データ取得を行う簡単なプログラムを書いたので、
それを元にnode.jsのイベント駆動な部分とokuyamaのタグ機能についてを説明させていただこうかと思います。

※ node.jsとokuyamaの環境構築方法は特に書いていませんが、どちらも簡単に構築可能でした。


以下におおまかなデータ登録から取得までの流れを記述します。

サイバーエージェント 公式エンジニアブログ


まずはデータの登録の流れになります。

okuyama_setvalue.js
サイバーエージェント 公式エンジニアブログ-nodejs_okuyama_setvaluesource


[1] 登録するデータの作成 ~ 登録イベントの発行

1) でokuyamaにデータを登録するイベントとそのコールバックを受け取るイベントハンドラーをEventEmitterオブジェクトに登録しています。

L6 - L29 にかけて1 ~ 100 の数値に2~5の倍数を表すタグ情報を配列にセットしています。
たとえば10には"MULTIPLE_2" (2の倍数)と"MULTIPLE_5" (5の倍数)のタグが設定されます。

2)でokuyamaにデータを登録するイベントを発行しています


[2] okuyamaへのデータの登録要求 ~ 結果をコンソールに表示

2)で登録イベントが発行されると、イベントハンドラーに指定されているsetValueメソッドが呼ばれます。

3)でサーバに通信を行うSocketオブジェクトにデータ通信時に発行されるdataイベントのイベントハンドラーを追加しておきます。

4)でサーバへのリクエスト文字列※2を作成し、Socket#writeでサーバにリクエストを送信します。

L121にてJSON#stringifyでValue値を変換していますが、これはJavaScriptオブジェクトをJSON形式にシリアライズして、Value値にJavaScriptオブジェクトの登録も可能にしています。
サーバにリクエストが到達し、処理を終えるとSocketのdataイベントが発行され、3)で登録したイベントハンドラーが実行されます。
処理が成功していれば、イベントハンドラーの引数dataには"1,true,OK"という文字列が戻ってきます。


以上がokuyamaのデータ登録の大まかな流れになります。


※2 データ登録時のリクエスト文字列
Javaで実装する場合は、既にクライアントが提供されているので、特に実装する必要はないのですが、node.jsのクライアントはまだ発表されていないため、実装する必要があります。(2011/12/19時点)

[データ登録のリクエスト文字列]
要素をセパレータ文字","(カンマ)で連結します
データ登録の要素は

第1要素 : "1" (処理番号)
第2要素 : Key値 (Base64でエンコード)
第3要素 : Tag値 (Base64でエンコード) / Tag値が未指定時は"(B)"

Tag値は複数指定可能。複数指定する場合はBase64でエンコードしたTag値をタグセパレータ文字列":"で連結する。

第4要素 : "0"固定(分散ロック値)
第5要素 : Value値 (Base64でエンコード)
第6要素 : バージョンによっては有効期限が使用可能

その他、キーによるデータ取得などにもリクエスト文字列が必要になってきますが、以下のサイトにまとめられているので、そちらを参考になるかと思います。
okuaymaのオリジナルプロトコルの仕様表



続いては、タグを指定したデータ取得の流れです。

okuyama_getvalue.js
サイバーエージェント 公式エンジニアブログ-nodejs_okuyama_getvaluesource


[1] Value取得イベントの登録 ~ タグに紐づくKey値リストの取得

1) okuyamaからKeyに紐づくValue値を取得するイベントとそのコールバックを受け取るイベントハンドラーをEventEmitterオブジェクトに登録しています。

L5で"MULTIPLE_5"のタグを指定して、タグに紐づく全てのキー(Value値が5の倍数のデータのキー)を取得するメソッドを呼び出しています。

2)でサーバに通信を行うSocketオブジェクトにデータ通信時に発行されるdataイベントのイベントハンドラーを追加しておきます。

3)でサーバへのリクエスト文字列※2を作成し、Socket#writeでサーバにリクエストを送信します。

サーバにリクエストが到達し、処理を終えるとSocketのdataイベントが発行され、2)で登録したイベントハンドラーが実行され、処理が成功していればタグに紐づくキーのリストが取得できます。


[2] okuyamaへのValue値の取得要求 ~ Value値の取得結果をコンソールに表示

3)でキーが取得されるとL48で取得したKey数分だけValue値取得のイベントが発行されます。

3)でValue値取得イベントが発行されると、イベントハンドラーに指定されているgetValueメソッドが呼ばれます。

4)でサーバに通信を行うSocketオブジェクトにデータ通信時に発行されるdataイベントのイベントハンドラーを追加しておきます。

4)でサーバへのリクエスト文字列※2を作成し、Socket#writeでサーバにリクエストを送信します。

サーバにリクエストが到達し、処理を終えるとSocketのdataイベントが発行され、4)で登録したイベントハンドラーが実行されます。
処理が成功すれば、Key値に紐づくValue値が取得できます。


実行結果は以下の通りです。
サイバーエージェント 公式エンジニアブログ

1~100のValue値のうち、5の倍数のものだけが取得できています。



■感想

・node.js

今までjavascriptでの実装経験がなかったのですが、思ったよりも簡単に実装することが出来ました。
Adobe flex/AIR での実装経験があったので、イベント駆動型に慣れていたのもあるかもしれませんが。。。
ライブラリモジュールも充実しており、ある程度のことならライブラリモジュールを使って簡単に実現できそうです。
最近になってWindows版も出てきていて、ローカルでのデバックも非常に容易になっており、手軽にはじめられそうです。
ただ、非同期処理がどうしても増えるので、設計時は気を使わないと、タイミングによって発生するバグとかが発生しがちになるかと思います。


・okuyama

タグ機能は非常に強力な機能だと思います。
この機能を使えば、KVSの適用範囲も使い勝手もぐっと広がるんじゃないかなと思います。
また今回はnode.jsと連携させましたが、Javaでは前述の通りクライアントが既に提供されており、様々な機能が使えるようになっているので、Javaでの実装は非常に簡単になるかと思います。
実際にJavaのクライアントのソースを見てみましたが、複数タグを指定してANDまたはORで条件を絞り込んでいる機能や、自動接続(生きているマスターノードを探して自動的に接続する)なども実装されていました。

※ ソースのコメントも日本語で書かれているので、非常に分かりやすかったです。

他にもデータノードでのJavaScript実行など気になる機能も色々あるので、もう少し色々な機能を使い込んでみたいですね。
あとは100%Javaで組まれていて、どこまでパフォーマンスが出るのかも気になるので、そのうちパフォーマンスについても調査してみたいなと思います。