要約
分散オブジェクトプログラミングのためのライブラリです。
Ruby のプロセスから他のRubyプロセスにあるオブジェクトのメソッドを呼びだすことができます。他のマシン上のプロセスにもアクセスできます。
概要
dRuby は Ruby 専用の分散オブジェクトシステムです。 Ruby のみで記述され、TCP socket のような Ruby 本体が提供する通信手段があれば追加のインストール物なしに利用可能です。独自のプロトコルで通信し、他の分散オブジェクトシステム (CORBA, RMI, .NETなど)との相互運用性はありません。
dRuby は
- 他のプロセスと Ruby オブジェクトのリファレンスをやりとりすること
- そこからのメソッド呼び出し
- メソッド呼出の引数/返り値を Marshal でバイト列に変換(マーシャリング) して通信先のプロセスと受け渡しすること
ができます。これらはすべて透過的に行われます。
リモートプロセスにあるオブジェクトはローカルには DRb::DRbObject のインスタンスとして表現されます。このオブジェクトはリモートオブジェクトの proxy のように振舞います。つまり、このオブジェクトのメソッドを呼び出すとリモートオブジェクトに転送されます。 CORBA の IDL のようなリモートオブジェクトのインターフェースを静的に宣言する必要はなく、すべては実行時に解決されます。
リモートプロセスからのメソッド呼出しはそれを受け取ったプロセスの DRb::DRbServer オブジェクトが処理します。受け取ったメッセージからメソッド呼出し情報を取り出し、ローカルにあるオブジェクトを特定し、そのメソッドを呼び出し、返り値をリモートの呼び出し元に送ります。どのようなオブジェクトのメソッドも呼びだすことができます。何か特別なインターフェースを実装したり、特別な mixin を必要としたりはしません。オブジェクトの特定は DRb::DRbServer が自動でします。そのためオブジェクトの登録のようなことは通常必要ありません。
DRb::DRbServer に URI(例: druby://example.com:8787)を関連付けることで、他のプロセスからの通信(リモートメソッド呼び出し)ができるようになります (逆に言うと、URIを指定しないことで、他のプロセスからのリモートメソッド呼び出しを拒否することができます)。また、DRb::DRbServer に「フロントオブジェクト」を登録しておくと、サーバの URI からそのオブジェクトをリモートオブジェクトとして取り出すことができます。通常はこのオブジェクトから辿って必要な(リモート)オブジェクトを取り出します。
リモートメソッド呼び出しはかなりの部分、同じプロセス内のオブジェクトのメソッドを呼び出すのと同じ動作をします。ブロック付きのメソッド呼び出しもできますし、リモートプロセス上で生じた例外はローカルプロセス上に転送されます。DRb 関連の例外は DRb::DRbError のサブクラスです。
リモートメソッド呼び出しの引数や返り値には任意の Ruby オブジェクトが使えます。デフォルトではオブジェクトをマーシャリングして渡され、受け取った側が元のオブジェクトに戻します。つまりオブジェクトはコピーされます。これは通常の同一プロセス上でのメソッド呼び出しと大きく異なる点です(通常のメソッド呼び出しではオブジェクトへのリファレンスが渡されます)。
ただし、マーシャリング不可能なオブジェクトは dRuby によってある種のリファレンスとして取り扱われます。これは DRb::DRbObject のインスタンスとして表現されます。これはリモートオブジェクトの proxy として動作し、proxy のメソッドを呼び出すと上に説明した通りの方法でリモートオブジェクトのメソッドを呼び出します。
マーシャリング可能なオブジェクトを DRbObject でリファレンスとして渡したい、つまりコピーでなくリファレンスで渡したい場合はそのオブジェクトに DRb::DRbUndumped を Module#include します。
dRuby はブロック付きのメソッド呼び出しをサポートしていますが、 Proc はマーシャリング不可能なので、ブロックの中身は (リモート側でなく)ローカルプロセス上で実行されます。リモート側がブロックを呼び出そうとすると、ブロックの引数がリモート側からローカル側に(上で説明したようにコピーもしくは dRuby のリファレンスオブジェクトとして)渡され、ブロックが実行され、その返り値がリモート側に送られます。
セキュリティ
dRuby でインターネット上に公開するサービスを作るべきではありません。イントラネットのサービスとして動かす場合もセキュリティには気を使う必要があるでしょう。
あるオブジェクトへの外部からのアクセスを許可すると、単にそのオブジェクトのメソッドを外部から呼び出せるだけでなく、任意の Ruby のコードを実行できてしまいます。例えば以下のようなことができます。
# !! 危険 !! ro = DRbObject.new_with_uri("druby://your.server.com:8989") class << ro # リモートオブジェクトの instance_eval を呼ぶため # ローカルオブジェクトの instance_eval を取り除く undef :instance_eval end ro.instance_eval("DANGEROUS RUBY CODE!")
このような instance_eval による危険性は $SAFE を 1 にすることで防げます。 DRb.#start_service の :safe_level オプションでリモートからのメソッド呼び出しのコンテキストで指定されるセーフレベルを指定できます。
また、DRb::DRbServer にはアクセスコントロールリスト(アクセスを許可/拒否する IP のリスト)によりアクセス制御をすることができます。この機能は ACL で実現されています。このアクセス制御は単体で使うのではなく、適切なファイアウォールと併用すべきです。
リファレンス
- http://www2a.biglobe.ne.jp/~seki/ruby/druby.html
- http://www.ruby-doc.org/stdlib/libdoc/drb/rdoc/index.html
Example
単純なクライアント-サーバシステムの例。
ターミナルを2つサーバ側/クライアント側として起動して、サーバ側を先に動かしてください。
サーバ側コード
require 'drb/drb' # 通信を待ち受ける URI SERVER_URI="druby://localhost:8787" class TimeServer def get_current_time return Time.now end end # サーバ側でリクエストを受け付けるオブジェクト FRONT_OBJECT=TimeServer.new # サーバを起動する DRb.start_service(SERVER_URI, FRONT_OBJECT, :safe_level => 1) # DRb のスレッドが終了するのを待つ DRb.thread.join
クライアント側コード
require 'drb/drb' # 接続先の URI SERVER_URI="druby://localhost:8787" # DRbサーバを起動する # この例には必要ないが、front オブジェクト以外の # リモートオブジェクトのメソッドを呼び出す時には必要 DRb.start_service # リモートオブジェクトの取得 timeserver = DRbObject.new_with_uri(SERVER_URI) # リモートメソッドの呼び出し puts timeserver.get_current_time
クラス
DRb::DRbIdConv | オブジェクトと識別子を相互に変換するクラスです。 |
DRb::DRbObject | リモートの dRuby オブジェクトを表すオブジェクトです。 |
DRb::DRbServer | dRuby サーバクラス。 |
DRb::DRbUnknown | リモートプロセスからマーシャリングされて送られてきたオブジェクトで、そのクラスがローカルプロセス内では不明であるようなものを表すクラス。 |
DRbIdConv | Alias of DRb::DRbIdConv |
DRbObject | Alias of DRb::DRbObject |
モジュール
DRb | drb ライブラリの名前空間となるモジュール。 |
DRb::DRbProtocol | drb で使われる通信プロトコルを取り扱うモジュールです。 |
DRb::DRbUndumped | このモジュールをインクルードしたクラスのインスタンスはネットワーク越しに参照渡しで渡されるようになります。値渡し出来ないオブジェクトを drb と一緒に使う時に有用です。 |
DRbUndumped | Alias of DRb::DRbUndumped |
例外クラス
DRb::DRbError | drb ライブラリ固有の例外を表すクラス |
DRb::DRbBadScheme | プロトコルクラスが受け取った URI の schema がそのクラスでサポートされていないことを、伝えるための例外。 |
DRb::DRbBadURI | URI に含まれている schema をサポートしているプロトコルが見付からないことを意味する例外クラス。 |
DRb::DRbConnError | 通信エラーが発生したことを意味する例外クラス。 |
DRb::DRbRemoteError | 例外オブジェクトを wrap したクラス |
DRb::DRbServerNotFound | カレントサーバが見付からない場合に発生する例外のクラス |
DRb::DRbUnknownError | DRb::DRbUnknown をラップする例外クラスです。 |
サブライブラリ
drb/acl | drb で用いる ACL(Access Control List)を定義するライブラリ。 |
drb/extserv | DRb::ExtServ を定義しているライブラリ。 |
drb/extservm | DRb::ExtServManager を定義しているライブラリ。 |
drb/gw | drb 通信を中継するゲートウェイ(DRb::GW)と、中継に必要なオブジェクト識別子変換クラス(DRb::GWIdConv)、および DRb::DRbObject への拡張が含まれています。 |
drb/observer | DRb 用の修正をした Observable (DRb::DRbObservable) を定義するライブラリ。 |
drb/ssl | DRb のプロトコルとして SSL/TLS 上で通信する drbssl が使えるようになります。 |
drb/timeridconv | DRb::DRbIdConv の拡張 DRb::TimerIdConv を定義するライブラリ。 DRb::DRbIdConv の GC 問題をタイムアウトを定めることで部分的に解決します。 |
drb/unix | DRb のプロトコルとして UNIX ドメインソケット経由で通信する drbunix が使えるようになります。 |