DRuby別名DRb-Rubyの分散システムの基瀎。 仕事の原理ず萜ずし穎

最近、dRubyブック-Rubyによる分散䞊列コンピュヌティングラむブラリの䜜成者自身が曞いた日本語の本の翻蚳がリリヌスされたした。 この蚘事では、DRbラむブラリに関する本の章の抂芁を説明したす。 このトピックをさらに詳しく知りたい堎合は、本を賌入たたはダりンロヌドできたす 。 この投皿ではスレッドの同期に぀いおも、Rindaラむブラリに぀いおも説明したせん。

耇数のプロセスで動䜜するシステムを䜜成しおいるずしたす。 たずえば、バックグラりンドで長時間実行されるタスクを実行するWebサヌバヌがありたす。 たたは、あるプロセスから別のプロセスぞのデヌタの転送を確実にし、それらを調敎する必芁がありたす。 このような状況では、DRbラむブラリが必芁です。 完党にRubyで蚘述されおおり、暙準ラむブラリに含たれおいるため、すぐに䜜業を開始できたす。 接続するには、 require 'drb'曞くだけrequire 'drb'

DRbラむブラリの長所は、䞻にRuby蚀語自䜓のダむナミズムにありたす。
第䞀に、準備段階で最小限の劎力を費やす堎合、あるプロセスたたは別のプロセスで、ためらうこずなくオブゞェクトのある堎所で䜜業したす。 ラむブラリは、すべおの技術的な詳现を完党に隠しおいたす。
第二に、むンタヌフェむスをハヌドコヌドする必芁はありたせん。 rubyオブゞェクトは、そのむンタヌフェむスを倖郚に公開できたす。この方法では、どちらもHashやQueueなどの暙準クラスのいずれかの機胜を䜿甚したり、任意のむンタヌフェむスで独自のクラスを䜜成したりできたす。 さらに、実行䞭にむンタヌフェむスを盎接倉曎したり、 method_missingを䜿甚しおmethod_missingを凊理するこずもできたす。 そしおもちろん、クラむアントが眲名たたは動䜜を倉曎したメ゜ッドを呌び出さない堎合、サヌバヌむンタヌフェむスの曎新はクラむアントにたったく圱響したせん。 したがっお、サヌバヌずクラむアントは可胜な限り独立しおいたす。
そしお最埌に、クラむアントはサヌバヌから返されるオブゞェクトのクラスを知る必芁さえありたせん。クラむアントはそれを䜿甚せずに䜿甚できたす。 したがっお、サヌバヌは必芁なだけ詳现を非衚瀺にできたす。
しかし、もちろん、萜ずし穎があり、それらはたくさんありたす。 幞い、dRubyの理解は簡単ですが、その構造を理解するこずで、ほずんどの問題を簡単に防ぐこずができたす。 残念ながら、このラむブラリのドキュメントでは倚くの点が明確にされおいないため、この蚘事は初心者ず既にラむブラリを䜿甚したこずがある人にずっお興味深いものになりたす。

すべおが機胜するこずを確認するには、2぀のirb端末を開きたす。 ルビヌ1.8の違いがどれほど倧きいかわからないので、バヌゞョン1.9に぀いお議論しおいるこずに同意したしょう特に1.8以降-すぐにサポヌトが終了したす

条件付きで、これら2぀の端末はサヌバヌずクラむアントです。 サヌバヌは、リク゚ストを受信するフロントオブゞェクトを提䟛する必芁がありたす。 このオブゞェクトには、組み蟌み型のオブゞェクト、特別に䜜成されたむンタヌフェむスを持぀モゞュヌルなど、任意のオブゞェクトを指定できたす。 次に、クラむアントはサヌバヌに接続し、このオブゞェクトず察話したす。
たずえば、最初の端末でサヌバヌを起動し、通垞の配列を配眮したしょう。

 require 'drb' front = [] DRb.start_service('druby://localhost:1234', front) front << 'first' #       ,    -    DRb.thread.join 

クラむアントを接続したす。 配列の最初の芁玠を認識し、別の芁玠を配列に曞き蟌みたす

 require 'drb' DRb.start_service remote_obj = DRbObject.new_with_uri('druby://localhost:1234') p remote_obj p remote_obj[0] remote_obj << 'second' 


これで、最初の端末からfront[1]呌び出しお、文字列'second'たす。 たた、別のクラむアントを接続し、そこから同じ正面オブゞェクトを操䜜するこずもできたす。

既にDRb.start_serviceに、サヌバヌはDRb.start_serviceコマンドによっお起動されたすレゞスタに泚意しおください。 このメ゜ッドは、匕数ずしお'druby://hostname:port'ずいう圢匏'druby://hostname:port'アドレスずフロント゚ンドオブゞェクトを持぀文字列を取りたす。 フロント゚ンドオブゞェクトは、リク゚ストを受信するオブゞェクトです。
サヌバヌが別のスクリプトで起動したら、スクリプトの最埌にDRb.thread.joinを蚘述したす。 実際、DRbサヌバヌは別のスレッドで起動され、メむンスレッドが完了するずすぐにRubyがプログラムをシャットダりンしたす。 したがっお、メむンスレッドがDRbサヌバヌストリヌムのクロヌズを埅たない堎合、䞡方のストリヌムがスケゞュヌルより早く完了し、サヌバヌはすぐに利甚できなくなりたす。 DRb.Thread.joinメ゜ッドを実行するず、サヌバヌの電源が切れるたで珟圚のスレッドがブロックされるずいう事実に備えおください。

サヌバヌに接続するには、 DRbObject.new_with_uriメ゜ッドを呌び出し、サヌバヌが起動するアドレスを匕数ずしお枡す必芁がありたす。 このメ゜ッドは、 remote_objプロキシオブゞェクトを返したす。 プロキシオブゞェクトぞのリク゚ストメ゜ッド呌び出しは、リモヌトサヌバヌ䞊のオブゞェクトに自動的に転送され、呌び出されたメ゜ッドがそこで実行された埌、メ゜ッドを呌び出しおいるクラむアントに結果が返されたす。 ただし、すべおのメ゜ッドがサヌバヌで呌び出されるわけではありたせん。たずえば、動䜜によっお刀断するず、 #classメ゜ッド#classロヌカル#class実行されたす
クラむアントは、 DRb.start_serviceコマンドの意味DRb.start_service少し埌で説明したす。

最埌に、リモヌトオブゞェクトのメ゜ッドがどのように実行されるかを理解したしょう。 これを行うには、プロキシオブゞェクトのメ゜ッドを呌び出すず、メ゜ッドの名前ず匕数のリストがシリアル化マヌシャリングされ、TCPプロトコルを䜿甚しお結果の文字列がサヌバヌに転送されたす。サヌバヌは呌び出し匕数を逆シリアル化し、フロント゚ンドオブゞェクトでメ゜ッドを実行し、結果をシリアル化しおクラむアントに返したす。 すべおがシンプルに芋えたす。 実際、通垞のリモヌトオブゞェクトず同じ方法でリモヌトオブゞェクトを操䜜し、メ゜ッド、プロキシオブゞェクト、およびサヌバヌのリモヌト実行のための倚くのアクションが非衚瀺になりたす。

しかし、それほど単玔ではありたせん。 リモヌトメ゜ッドの呌び出しは高䟡です。 サヌバヌ䞊のメ゜ッドが倚くの匕数メ゜ッドを「プル」するこずを想像しおください。 これは、サヌバヌずクラむアントが蚈算を行う代わりに、時間の倧郚分が比范的遅いプロトコルを䜿甚しお互いにアクセスするこずを意味したす2぀のプロセスが同じマシンに配眮されおいるず䟿利です。 これを防ぐために、プロセス間の匕数ず結果は参照ではなく倀で枡されたすオブゞェクトのマヌシャラむれヌションはオブゞェクトの内郚状態のみを栌玍し、そのobject_id認識したせん-したがっお、オブゞェクトは最初にシリアル化され、その埌、逆シリアル化は元のオブゞェクトのコピヌになりたす、同じオブゞェクトによるものではないため、転送はコピヌによっお自動的に行われたす。 Rubyでは通垞、すべおが参照によっお枡され、dRubyでは通垞は倀によっお枡されたす。 したがっお、 front[0].upcase!を実行するず サヌバヌでは、 front[0]倀が倉曎され、 remote_obj[0].upcase!を実行するず 、最初の芁玠を倧文字で取埗したすが、 remote_obj.[](0)は最初の芁玠のコピヌであるため、サヌバヌ䞊の倀は倉曎されたせん。 この呌び出しは、 front[0].dup.upcase!ず同様ず芋なすこずができたすfront[0].dup.upcase!
ただし、匕数ず結果を参照で枡すような方法でdRubyの動䜜をい぀でも定矩できたすが、これに぀いおは埌で詳しく説明したす。

今こそ、最初の問題に぀いお話し合うずきです。 すべおのオブゞェクトが敎列化されるわけではありたせん。 たずえば、ProcおよびIOオブゞェクト、およびスレッドスレッドオブゞェクトは、コピヌを介しおマヌシャリングおよび送信できたせん。 この堎合のdRubyは次のように進みたす。マヌシャラむれヌションが機胜しなかった堎合、オブゞェクトは参照枡しされたす。
では、オブゞェクトはどのように参照枡しされたすか Cを思い出しおください。 そこでは、ポむンタヌがこの目的に䜿甚されたす。 Rubyでは、ポむンタヌの圹割はobject_idによっお実行されたす。 オブゞェクトを参照枡しするには、 DRbObjectクラスのオブゞェクトがDRbObjectたす。
実際、 DRbObjectは参照枡しのプロキシオブゞェクトです。 このクラスのむンスタンスDRbObject.new(my_obj)にobject_idオブゞェクトmy_obj object_idオブゞェクトのmy_objのサヌバヌのURIが含たれおいたす。 これにより、メ゜ッド呌び出しをむンタヌセプトしお、メ゜ッドが意図されたリモヌトマシンたたは別の端末䞊のオブゞェクトに枡すこずができたす。

サヌバヌをメ゜ッドにしたしょう

 def front.[](ind) DRbObject.new(super) end 

そしお、クラむアントからコヌドを実行したす。

 remote_obj.[0].upcase! 


新しいメ゜ッド#[]は、最初の芁玠のコピヌではなく、リンクを返したした。そのため、 upcase!メ゜ッドを実行した埌にupcase! フロントオブゞェクトが倉曎された堎合、これは、たずえばクラむアントずサヌバヌからそれぞれputs remote_objたたはputs frontコマンドを実行するこずで簡単に確認できたす。

しかし、 DRbObject.newを曞くDRbObject.new -怠DRbObject.newです。 幞いなこずに、倀ではなく参照によっおオブゞェクトを枡す別の方法がありたす。 これを行うには、オブゞェクトを非敎列化可胜にすれば十分です。 これは簡単で、オブゞェクトにDRbUndumpedモゞュヌルを含めるだけです。
 my_obj.extend DRbUndumped class Foo; include DRbUndumped; end 

これで、オブゞェクトmy_objずFooクラスのすべおのオブゞェクトが参照によっお自動的に枡されたすそしおMarshal.dump(my_obj)はTypeError 'can\'t dump'をTypeError 'can\'t dump'たす。

実際に出䌚った䟋を挙げたしょう。 サヌバヌはハッシュをフロントオブゞェクトずしお蚭定したす。この堎合、倀はチケットですチケット内からはステヌトマシンです。 次に、 remote_obj[ticket_id]はチケットのコピヌを提䟛したす。 ただし、これによりサヌバヌ䞊のチケットステヌタスを倉曎するこずはできず、ロヌカルでのみ倉曎できたす。 TicketクラスにDRbUndumpedを取埗したしょう。 これで、ハッシュからチケットのコピヌではなく、チケットぞのリンクが埗られたす。そしお、それに察するアクションは、クラむアント䞊ではなくサヌバヌ䞊で盎接発生したす。

そしお今、玄束を思い出しお、クラむアントでDRb.start_serviceを呌び出す必芁がある理由を教えおください。 最初の䟋のように、サヌバヌ䞊のフロントオブゞェクトによっお配列が瀺されるこずを想像しおください。
ここで、クラむアントがremote_obj.map{|x| x.upcase}メ゜ッドを呌び出したすremote_obj.map{|x| x.upcase} remote_obj.map{|x| x.upcase}
実際、ブロック匕数を持぀mapメ゜ッドは、フロントオブゞェクトで呌び出されたす。 そしお、私たちが思い出すように、私たちはマヌシャリングできたせん。 したがっお、このブロック匕数は参照枡しされたす。 サヌバヌ䞊のmapメ゜ッドはyieldでアクセスしたす。これは、クラむアントがサヌバヌであるこずを意味したす ただし、クラむアントは時々サヌバヌである必芁があるため、 start_serviceメ゜ッドを䜿甚しおDRbサヌバヌも起動する必芁がありたす。 このサヌバヌのURIを指定する必芁はありたせん。 内郚からどのように機胜するか、私にはわかりたせんが、機胜したす。 そしお、すでにお気付きのように、クラむアントずサヌバヌの違いは芋た目よりも小さいです。

新しい迷惑に぀たずくリスクがありたす。 メ゜ッドが、メ゜ッドで盎接生成されたオブゞェクトぞのリンクコピヌではなくを返したずしたす。 サヌバヌがこのオブゞェクトを別の堎所に個別に保存しなかった堎合たずえば、特別なハッシュに入れなかった堎合、サヌバヌぞのリンクはありたせん。 リモヌトマシン䞊のクラむアントは持っおいたすが、サヌバヌは持っおいたせん したがっお、遅かれ早かれ、このオブゞェクトのメヌルのためにトロヌルがあなたのずころに来たす。GC-ガベヌゞコレクタヌです。 これは、しばらくするず、クラむアントのDRbObjectようなリンクが「䞍良」になり、どこにもリンクされないこずを意味したす。 このオブゞェクトのメ゜ッドにアクセスしようずするず、゚ラヌが発生したす。
したがっお、少なくずもサヌバヌによっお䜿甚されるたで、返されたオブゞェクトぞのリンクをサヌバヌが保管するように泚意する必芁がありたす。 これにはいく぀かの解決策がありたす。
1参照によっお枡されたすべおの返されたオブゞェクトを配列に保存したす-リンクが䜿甚されるため、ガベヌゞコレクタヌはそれらを収集したせん。
2クラむアントにブロックぞのリンクを送信したす。 䟋
このコヌドの代わりに
 Ticket.send :include, DRbUndumped def front.get_ticket Ticket.new end foo = remote_obj.get_ticket foo.start foo.closed? #   foo       . ,   start   . 


次のように曞かなければなりたせん
 Ticket.send :include, DRbUndumped def front.get_ticket object_to_reference = Ticket.new yield object_to_reference end remote_obj.get_ticket do |foo| foo.start foo.closed? end 

サヌバヌ䞊の有効なロヌカル倉数は、ガベヌゞコレクタヌによっお収集できたせん。 したがっお、ブロック内では、リンクが機胜するこずが保蚌されおいたす。
3この本は別の方法を説明しおいたす-オブゞェクトのobject_id受け取る段階でリンクを䜜成するプロセスにobject_idこの時点でガベヌゞコレクションプロセスを䜕らかの方法で遅らせる必芁がありたす。 ハッシュに芁玠を自動的に远加し、オブゞェクトを氞久に保存するこずができたす掚枬できるように、メモリは遅かれ早かれ䜿い果たされるでしょう、オブゞェクトぞのリンクを保存しお手動でクリアできたす。このハッシュは数分ごずにクリアできたす。
最埌のメ゜ッドは、次のようにしお実装できたす。
 require 'drb/timeridconv' DRb.install_id_conv(DRb::TimerIdConv.new) 

サヌバヌを起動する前に。 詳现に぀いおは、本の第11ç« -ガベヌゞコレクションの凊理を参照しおください。 私には興味深いようです。おそらくそれを読んだ埌、ガベヌゞコレクションプロセスの操䜜を䜿甚する新しい方法が芋぀かるでしょう。 それでも、実際には2番目の方法を䜿甚しお、ブロックぞのリンクを䞎える方が良いず思いたす。 より信頌性が高く理解しやすい。

おそらく最埌の瞬間を照らすために残っおいたす。 Fooオブゞェクトをリンクずしお枡すず仮定したす。 クラむアントはFooクラスに぀いおは知りたせんが、それでもオブゞェクトでの動䜜を劚げるこずはありたせん。 基本的に、クラむアントはDRbObjectクラスのオブゞェクトを操䜜したす。 すべおがい぀も通りです。
ここで、リンクではなくコピヌを送信しおいるず想像しおください。 サヌバヌでのシリアル化により、オブゞェクトの状態ずそのクラスの名前が保存されたした。 クラむアントは文字列を受信し、それをデシリアラむズしようずしおいたす。 もちろん、クラむアントは存圚しないクラスFooオブゞェクトを䜜成できないため、これは圌には機胜したせん。 次に、逆シリアル化DRb::DRbUnknown 、 DRb::DRbUnknown型のオブゞェクトが返され、マヌシャリングされたオブゞェクトず共にバッファヌが栌玍されたす。 このオブゞェクトはたずえば、タスクキュヌに枡すこずができたす。 たた、クラスの名前を確認し、適切なラむブラリをクラスにロヌドしお、 reloadメ゜ッドを呌び出すこずもできたす。その埌、逆シリアル化が再床詊行されたす。 それは

いいえ、ただこれが最埌の瞬間ではありたせん。 同期に぀いおは曞かないこずを玄束したしたが、それでもいく぀か蚀葉を述べたす。
分散プログラミングの堎合、アクションの同期ず操䜜の原子性は重芁な抂念です。 サヌバヌは別のスレッドで起動したす。 サヌバヌぞのリク゚ストごずに、このリク゚ストが凊理される個別のストリヌムが自動的に䜜成されたす。 したがっお、異なるスレッドが同じ情報に同時にアクセスするこずを犁止する必芁がありたす。 そのため、分散システムおよび䞊列システムをプログラミングする堎合は、次を䜿甚したす。
1構築lock = Mutex.new; lock.synchronize{ do_smth } lock = Mutex.new; lock.synchronize{ do_smth }
2暙準MonitorMixinラむブラリのモゞュヌル
3暙準ラむブラリクラスのQueue 、 SizedQueue

DRbを䜿甚しお頑匵っおください 砎壊的な方法を䜿甚しおいるにもかかわらず、オブゞェクトが倉曎されない理由、ブロックを受け入れる方法をクラむアントで機胜させる方法、受信したリンクが機胜し動䜜した理由を理解しようずしお、誰かが長い時間を費やさないように願っおいたす。
ただし、この本では、特にRindaラむブラリずその同等物に぀いお、さらに倚くを芋぀けるこずができたす。

Source: https://habr.com/ru/post/J143671/


All Articles