Google が開発する KVS の LevelDB はライブラリーとしてのみ提供されており、そのままではクライアント-サーバ型では使えない。
There is no client-server support builtin to the library. An application that needs such support will have to wrap their own server around the library.
http://code.google.com/p/leveldb/
ググったところ、ØMQ(zeromq) を使ってサーバサイドを実装した LevelDB Server というプログラムがあった。ØMQ のお勉強を兼ねて実装をメモ。
LevelDB Server 概要
- 2011年11月に github 公開。その後、放置(LevelDB は2011年7月に公開。その後、定期的に更新。)
- URL : https://github.com/srinikom/leveldb-server
- ネットワークプレイヤーにはZeroMQを採用
- DB 操作はワーカースレッドが行う
- メッセージングパターンは client to server は Dealer to Router、server to worker が Dealer to Dealer で両方とも非同期
- データのシリアライズには
JSON
を利用 - Python で実装
メッセージングパターン概要
ØMQ のマニュアルと付き合わせながら、LevelDB Server で利用されているメッセージングパターン(Dealer to Router, Dealer to Dealer)を確認してみる。
Request-Reply パターン
LevelDB Server で利用されている Dealer to Router パターンと Dealer to Dealer パターンはともに Request-Reply パターンがベースになっている。
Figure 1 – Request-Reply
Request to Router パターン
Request-Reply パターンにおいて、サーバが非同期に複数クライアントを捌けるようにすると Request to Router パターンになる。
サーバの Router はクライアントがリクエストした時に発生する POLLIN
イベントを監視し、イベントをトリガーにして、非同期に処理を行う。
Dealer to Reply パターン
Request-Reply パターンにおいて、サーバが非同期に複数のワーカースレッドを捌けるようにすると Dealer to Reply パターンになる。
サーバの Dealer はワーカースレッドがリプライした時に発生する POLLIN
イベントを監視し、イベントをトリガーにして、非同期に処理を行う。
Request to Router パターンと Dealer to Replay パターンを組み合わせると、 Request-reply Broker パターンになる。
Figure 17 – Request-reply Broker
サーバの Router-Dealer 間は ZeroMQ のスレッド間通信(inproc
プロトコル) を介する。
Dealer to Router パターン
クライアント-サーバ間の処理でサーバサイドだけでなく、クライアントも非同期に処理を行うと Dealer to Router パターンになる。
クライアントはサーバへの送信を行いつつ、サーバからのリプライがあった時に発生する POLLIN
イベントを監視し、イベントをトリガーにして、非同期に処理を行う。
Figure 37 – Asynchronous Client-Server
Dealer to Dealer パターン
サーバ-ワーカー間の処理でサーバサイドだけでなく、ワーカーも非同期に処理を行うと Dealer to Dealer パターンになる。
ワーカーはサーバへのリプライを行いつつ、サーバからデータ受信があった時に発生する POLLIN
イベントを監視し、イベントをトリガーにして、非同期に処理を行う。
Dealer to Router パターンと Dealer to Router パターンを組み合わせると完全非同期の出来上がり。LevelDB Server ではこのメッセージングパターンが使われている。
Figure 38 – Detail of Asynchronous Server
チュートリアルにある Asynchronous Server に対応するサンプルコード : http://zguide.zeromq.org/py:asyncsrv
Request-Reply Combinations
Requeset-Reply パターンで利用されるソケットタイプ REQ/REP/ROUTER/DEALER
の組み合わせは、チュートリアル以下のセクションを参照。
http://zguide.zeromq.org/page:all#Request-Reply-Combinations
実際のコード
インストール方法
ドキュメントを参照 : https://github.com/srinikom/leveldb-server/blob/master/README.textile
ソケットタイプ
DEALER/ROUTER
は ZeroMQ の新しい用語らしく、コードではそれぞれの以前の呼び方の XREQ/XREP
で定義されている。
zmq_socket(3) によると、古い名称は Deprecated alias 扱いされている。
非同期処理
LevelDB-server ではクライアント⇔サーバー⇔ワーカースレッドはいずれも非同期処理に対応したメッセージングパターンで定義されている。
実際のコードを見てみると、サーバサイドは非同期に処理しているから良いとして、クライアントの処理は
def get(self, key): self.socket.send_multipart(['get', json.dumps(key)]) return self.socket.recv_multipart()[0]
というように send
と recv
を連続で呼び出していて、非同期になっていない。素直に Request to Router パターンで良かったのではないかと思う。
何か、勘違いしているのかな?
今後の拡張
メンテが止まっている LevelDB-server を利用する場合の注意点。
- ソースコードはクライアント・サーバ込で数百行しかないので、コード全体を理解するのは楽。
- 既知の問題はREADMEの Known issues and work in progress を参照
https://github.com/srinikom/leveldb-server#known-issues-and-work-in-progress JSON
を利用しているため、バイナリーデータを扱えない。バイナリーデータを扱う必要がある場合、シリアライズ処理を MessagePack や Google Protocol Buffers などに変える必要がある。
http://zeromq.github.com/pyzmq/serialization.html
References
- LevelDB : fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.
- ZeroMQ : high-performance asynchronous messaging library aimed at use in scalable distributed or concurrent applications.
- LevelDB Server : ZeroMQ ベースの LevelDB インターフェース。
put/get/delete/range
のみ対応 - LevelDB RPC : XML-RPC ベースの超テキトーな LevelDB インターフェース。
put/get/delete
のみ対応