)ネットワーク越しにオブジェクト(のメソッド)を呼び出せる分散オブジェクト技術。Rubyな実装であるdRubyで遊んでみる。Ruby.何が嬉しいの?スタンドアロン・プログラム分散オブジェクト化するオブジェクトをやり取りするひとつのサーバーで複数のリモートオブジェクトオブジェクトサーバーを分散させる何が嬉しいの?一つのプロセス内だとオブジェクトを操作するのは単にメソッドを呼び出すだけ。プロセスを跨ごうとすると,とたんにソケットだの何だのと,オブジェクトを分解して送信し,受信したら再びオブジェクトに復元しないといけなくなる。ネットワークの先にあるオブジェクトに直接アクセスできたらいいのに,と思う。これができるのが分散オブジェクトの嬉しさ。分散オブジェクトの構図を図にするとこんな感じ。[ネットワーク]-----[リクエスト]----->ネットワーク向こうにあるオブジェクト(リモートオブジェクト)を操作したいクライアントと,オブジェクトを実装するオブジェクト・サーバーとがある。ORBは,クライアントではスタブを,サーバーではスケルトンを生成する。スタブへのアクセスが発生すると,ORBはメソッド呼び出しを直列化してサーバーへ送る。オブジェクト自体をやり取りするときも,ORBが適切に変換・復元してくれる。スタンドアロン・プログラムまずはネットワークを介しないスクリプトを書いてみる。50クラスAccountは,口座を模したクラス。Account#depositメソッドでamountだけ預け入れ,Account#withdrawでamountだけ引き出す。Account#balanceでその時点の残高を得る。このスクリプトでは残高がマイナスになっても気にしない。分散オブジェクト化するこれをdRubyを利用して,ネットワークを介して呼び出すようにする。DRb.thread.joinsample-1.1のAccountクラスを流用する。動作するためには必要ないが,テストのためにどのプロセスで実行されたか知るためにメソッドhostを追加する。dRubyを使うときは,DRb.start_serviceクラスメソッドを最初に呼び出す。frontとしてオブジェクトを渡すと,それをリモートオブジェクトとして登録できる。DRb.start_serviceは,内部でDRbServerオブジェクトを生成し,それをプライマリサーバーとする。DRb.uriは,このオブジェクトにアクセスするためのURIを返す。クライアントでは,このURIによって,サーバーを識別する。上のサンプルは,AccountオブジェクトサーバーのURIを表示した後,アクセスを待つためにループに入る。DRb.uri()次はクライアント。#{Process.pid}\n"クライアントでも,最初にDRb.start_serviceを呼び出す。DRbObject.newで,スタブを生成する。DRbObjectインスタンスへのアクセスは,dRubyによってリモートオブジェクトへ送信される。19461サーバーオブジェクトはずっと生きているので,異なるクライアントからのアクセスであっても,同じオブジェクトが使い回される。オブジェクトをやり取りするAccountオブジェクトは,オブジェクトサーバーで活性化され,クライアントはサーバーにあるオブジェクトにアクセスできた。今度はオブジェクト自体をネットワーク越しに渡してみる。BalloonStoreクラスのインスタンスはサーバーで動かす。そのメソッドgetでBalloonクラスのインスタンスを生成し,Balloonインスタンス自体をクライアントに渡したい。endBalloonStore#getメソッドは,単にBalloonインスタンスを生成し,それを返す。#{host[1]}\n"リモートオブジェクトのgetメソッドを呼び出し,Balloonインスタンスを取得する。Balloon両方のホスト名などを表示する。"sample-3.1.rb"をコメントアウトすると,NameErrorが発生する。オブジェクトをやり取りするときは,クライアントでもそのオブジェクトのクラスの定義が必要。(NameError)Balloonの定義があれば,正常に動く。323BalloonStoreインスタンスはサーバーで動作し,Balloonインスタンスはクライアントで動作しているのが分かる。リモートオブジェクトは,サーバーのURIさえ分かれば,クライアントではクラスの定義を取り込む必要はない。オブジェクトをやり取りするときは,インスタンス変数などのみがやり取りされ,メソッドはやり取りされないので,クライアントの方でもそのオブジェクトのクラスの定義が必要となる。dRubyではURIでサーバーを識別するが,多くのリモートオブジェクトを使うときにも一つのURIで済ませたい。目的のリモートオブジェクトを生成するためだけのリモートオブジェクトを用意し,これを窓口にする。endほかのリモートオブジェクトを生成するための,Factoryクラスを設ける。このクラスのインスタンスをDRb.start_serviceメソッドで登録する。createCounterメソッドでCounterインスタンス,createReverseCounterメソッドでReverseCounterインスタンスを生成するが,このままだとリモートオブジェクトではなく,これらのインスタンスがそのままクライアントに渡されてしまう。Counterクラス,ReverseCounterクラスでDRbUndumpedモジュールをincludeすると,これらのクラスのインスタンスはリモートオブジェクトとして扱われる。end実行結果;3997FactoryクラスのcreateCounterメソッドなどを呼び出し,リモートオブジェクトへのスタブを取得する。あとは,それぞれのオブジェクトを普通に扱える。この例では,毎回リモートオブジェクトを生成しているが,プールしておいて,クライアントから呼び出されたときにそれを返せば,永続的なリモートオブジェクトも簡単に作れる。オブジェクトサーバーを分散させる今度は,複数のサーバーを用意して,クライアントからはどのサーバーがオブジェクトを提供しているか意識せずにリモートオブジェクトを扱えるようにしてみる。リモートオブジェクトを提供するサーバーを分散することで,一台当たりの負荷を低くしたり,サービスを停止することなくサーバーを入れ替えることができるかもしれない。目的のオブジェクトを提供するサーバーとは別に,どのリモートオブジェクトがどのサーバーで動いているかを管理する,ネームサービスオブジェクトを用意する。endNameServiceクラスは,オブジェクトの名前とそのオブジェクトへの参照とを対応付ける。次は,オブジェクトを提供するサーバー。ReverseCounterを流用する。endNameServiceオブジェクトを取得し,オブジェクトを登録する。endクライアントでは,いったんNameServiceオブジェクトを取得し,オブジェクトの名前からリモートオブジェクト(のスタブ)を取得する。ReverseCounterリモートオブジェクトを呼び出すとき,これらのオブジェクトを提供するサーバーと直接交信するので,NameServiceサーバーとの交信は,大きな負荷とはならない。実用化するには,成りすましを避ける工夫が必要。