3

SinatraアプリケーションとDRbサーバーオブジェクトがペアになっています。paramsSinatraハッシュをサーバーオブジェクトのメソッドに渡そうとすると、取得しますDRb::DRbConnError … DRb::DRbServerNotFoundが、単純なハッシュを直接渡すと同じメソッドが機能します。

  1. Sinatra paramsハッシュでこのエラーが発生するのはなぜですか?
  2. この問題を解決するための最も簡単で正しい回避策は何ですか?

簡単なテストケースは次のとおりです。

# server.rb
require 'drb'
class Server; def echo( hash ); hash; end; end
DRb.start_service 'druby://localhost:9007', Server.new
DRb.thread.join
# app.rb
require 'sinatra'
require 'drb'    
SERVER  = DRbObject.new_with_uri 'druby://localhost:9007'
get("/params"){ SERVER.echo(params).inspect        }
get("/hash"  ){ SERVER.echo(hello:'world').inspect }

これらの両方が独自のプロセスで実行されている場合:

phrogz$ curl http://localhost:4567/hash
{:hello=>"world"}

phrogz$ curl http://localhost:4567/params
DRb::DRbConnError - DRb::DRbServerNotFound:
 /usr/local/lib/ruby/1.9.1/drb/drb.rb:1653:in `current_server'
 /usr/local/lib/ruby/1.9.1/drb/drb.rb:1721:in `to_id'
 /usr/local/lib/ruby/1.9.1/drb/drb.rb:1050:in `initialize'
 /usr/local/lib/ruby/1.9.1/drb/drb.rb:642:in `new'
 /usr/local/lib/ruby/1.9.1/drb/drb.rb:642:in `make_proxy'
 /usr/local/lib/ruby/1.9.1/drb/drb.rb:559:in `rescue in dump'
 /usr/local/lib/ruby/1.9.1/drb/drb.rb:556:in `dump'
 /usr/local/lib/ruby/1.9.1/drb/drb.rb:603:in `block in send_request'
 /usr/local/lib/ruby/1.9.1/drb/drb.rb:602:in `each'
 /usr/local/lib/ruby/1.9.1/drb/drb.rb:602:in `send_request'
 /usr/local/lib/ruby/1.9.1/drb/drb.rb:903:in `send_request'
 /usr/local/lib/ruby/1.9.1/drb/drb.rb:1196:in `send_message'
 /usr/local/lib/ruby/1.9.1/drb/drb.rb:1088:in `block (2 levels) in method_missing'
 /usr/local/lib/ruby/1.9.1/drb/drb.rb:1172:in `open'
 /usr/local/lib/ruby/1.9.1/drb/drb.rb:1087:in `block in method_missing'
 /usr/local/lib/ruby/1.9.1/drb/drb.rb:1105:in `with_friend'
 /usr/local/lib/ruby/1.9.1/drb/drb.rb:1086:in `method_missing'
 app.rb:4:in `block in <main>'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:1152:in `call'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:1152:in `block in compile!'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:724:in `instance_eval'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:724:in `route_eval'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:708:in `block (2 levels) in route!'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:758:in `block in process_route'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:755:in `catch'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:755:in `process_route'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:707:in `block in route!'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:706:in `each'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:706:in `route!'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:843:in `dispatch!'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:644:in `block in call!'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:808:in `instance_eval'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:808:in `block in invoke'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:808:in `catch'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:808:in `invoke'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:644:in `call!'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:629:in `call'
 /usr/local/lib/ruby/gems/1.9.1/gems/rack-1.2.2/lib/rack/head.rb:9:in `call'
 /usr/local/lib/ruby/gems/1.9.1/gems/rack-1.2.2/lib/rack/commonlogger.rb:18:in `call'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/showexceptions.rb:21:in `call'
 /usr/local/lib/ruby/gems/1.9.1/gems/rack-1.2.2/lib/rack/methodoverride.rb:24:in `call'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:1272:in `block in call'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:1303:in `synchronize'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:1272:in `call'
 /usr/local/lib/ruby/gems/1.9.1/gems/rack-1.2.2/lib/rack/content_length.rb:13:in `call'
 /usr/local/lib/ruby/gems/1.9.1/gems/rack-1.2.2/lib/rack/chunked.rb:15:in `call'
 /usr/local/lib/ruby/gems/1.9.1/gems/thin-1.2.11/lib/thin/connection.rb:84:in `block in pre_process'
 /usr/local/lib/ruby/gems/1.9.1/gems/thin-1.2.11/lib/thin/connection.rb:82:in `catch'
 /usr/local/lib/ruby/gems/1.9.1/gems/thin-1.2.11/lib/thin/connection.rb:82:in `pre_process'
 /usr/local/lib/ruby/gems/1.9.1/gems/thin-1.2.11/lib/thin/connection.rb:57:in `process'
 /usr/local/lib/ruby/gems/1.9.1/gems/thin-1.2.11/lib/thin/connection.rb:42:in `receive_data'
 /usr/local/lib/ruby/gems/1.9.1/gems/eventmachine-0.12.10/lib/eventmachine.rb:256:in `run_machine'
 /usr/local/lib/ruby/gems/1.9.1/gems/eventmachine-0.12.10/lib/eventmachine.rb:256:in `run'
 /usr/local/lib/ruby/gems/1.9.1/gems/thin-1.2.11/lib/thin/backends/base.rb:61:in `start'
 /usr/local/lib/ruby/gems/1.9.1/gems/thin-1.2.11/lib/thin/server.rb:159:in `start'
 /usr/local/lib/ruby/gems/1.9.1/gems/rack-1.2.2/lib/rack/handler/thin.rb:14:in `run'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:1234:in `run!'
 /usr/local/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/main.rb:25:in `block in <module:Sinatra>'

これはOSX上のRuby1.9.2で実行されていますが、違いがあるとは思いません。

4

1 に答える 1

4

短い答え

追加する必要があります

DRb.start_service

リモート呼び出しをapp.rb試みる前に。

興味のある方への説明

Sinatra paramsハッシュは、欠落しているキーが参照された場合を処理するために、関連付けられたブロックを使用して作成されます(これがソースです)。これはProc、それに関連付けられたオブジェクトがあることを意味します。

Drbは、引数をマーシャリングすることによって引数を前後に渡します。ただし、元帥のドキュメントから:

一部のオブジェクトはダンプできません。ダンプするオブジェクトにバインディング、プロシージャまたはメソッドオブジェクト、クラスIOのインスタンス、またはシングルトンオブジェクトが含まれている場合、TypeErrorが発生します。

したがって、マーシャリング不可能なプロシージャオブジェクトが含まれているため、このparamsハッシュをネットワーク経由で渡そうとすると問題が発生します。

Drbドキュメントに:

ただし、オブジェクトをマーシャリングできない場合は、代わりにそのオブジェクトへのdRuby参照が渡されるか返されます。これは、リモートエンドでDRbObjectインスタンスとして表示されます。DRbObjectsの説明で説明されているように、このリモートプロキシで呼び出されたすべてのメソッドはローカルオブジェクトに転送されます。これは、通常のRubyの参照渡しと同様のセマンティクスを持っています。

良いニュースですが、それでも機能するはずです。では、何が問題なのですか?Drbドキュメントのもう少し先に、サンプルコードでこれを見つけます。

# Start a local DRbServer to handle callbacks.
#
# Not necessary for this small example, but will be required
# as soon as we pass a non-marshallable object as an argument
# to a dRuby call.
DRb.start_service

したがって、発生しているように見えるのは、Drbがプロシージャオブジェクトのリモート参照を取得してサーバーに渡そうとしているが、クライアント側にDrbサービスが設定されていないためにできないことです。


元の回答

(これはここに残しておきます。興味があるかもしれません。それは、すべてを理解するための私の旅の休憩ポイントでした。これは、代替ソリューションの可能性もあります。興味深いことに、私は今、私が紹介したより知識のある人になっているようです。少なくとも理由に関しては。)

考えられる回避策は次のとおりです。問題は、欠落しているキー(Sinatraのparamsハッシュ)を処理するためにブロックで作成されたハッシュにあるようです。そのため、ハッシュの内容を新しいものに抽出できます。どちらもprocを保持しているように見えますが(で確認できますparams.clone)、(または)はDrbで機能するクリーンなハッシュを提供します。params.merge({})Hash#default_proc{}.merge(params)merge!

したがって、この例では、次のようにします。

get("/params"){ SERVER.echo({}.merge params).inspect

なぜこれがDrbで発生し、procでハッシュされるのか、そしてこれが最も簡単な回避策であるか最も正しい回避策であるかどうか、私はもっと知識のある人に任せます。

于 2011-06-05T17:09:05.450 に答える