ØMQをつかってLevelDBをClient-Server型にする

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 で実装

メッセージングパターン概要

zeromq

Ø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

fig1:request-reply pattern

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

Figure 17 - Request-reply Broker

サーバの Router-Dealer 間は ZeroMQ のスレッド間通信(inproc プロトコル) を介する。

Dealer to Router パターン
クライアント-サーバ間の処理でサーバサイドだけでなく、クライアントも非同期に処理を行うと Dealer to Router パターンになる。
クライアントはサーバへの送信を行いつつ、サーバからのリプライがあった時に発生する POLLIN イベントを監視し、イベントをトリガーにして、非同期に処理を行う。

Figure 37 – Asynchronous Client-Server

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

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 を利用する場合の注意点。

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 のみ対応

Leave a comment