この説明と同様の理由で、あるRails3アプリケーションから別のアプリケーションへの同期RPC呼び出しのためにRESTの代わりにメッセージングを実験しています。どちらのアプリもシンで実行されています。
「サーバー」アプリケーションには、 rubyamqp.infoドキュメントconfig/initializers/amqp.rb
の要求/応答パターンに基づくファイルがあります。
require "amqp"
EventMachine.next_tick do
connection = AMQP.connect ENV['CLOUDAMQP_URL'] || 'amqp://guest:guest@localhost'
channel = AMQP::Channel.new(connection)
requests_queue = channel.queue("amqpgem.examples.services.time", :exclusive => true, :auto_delete => true)
requests_queue.subscribe(:ack => true) do |metadata, payload|
puts "[requests] Got a request #{metadata.message_id}. Sending a reply..."
channel.default_exchange.publish(Time.now.to_s,
:routing_key => metadata.reply_to,
:correlation_id => metadata.message_id,
:mandatory => true)
metadata.ack
end
Signal.trap("INT") { connection.close { EventMachine.stop } }
end
「クライアント」アプリケーションで、「サーバー」への同期呼び出しの結果をビューに表示したいと思います。これは、amqp gemのような本質的に非同期のライブラリの快適ゾーンから少し外れていることを認識していますが、それを機能させる方法があるかどうか疑問に思っています。これが私のクライアントですconfig/initializers/amqp.rb
:
require 'amqp'
EventMachine.next_tick do
AMQP.connection = AMQP.connect 'amqp://guest:guest@localhost'
Signal.trap("INT") { AMQP.connection.close { EventMachine.stop } }
end
コントローラは次のとおりです。
require "amqp"
class WelcomeController < ApplicationController
def index
puts "[request] Sending a request..."
WelcomeController.channel.default_exchange.publish("get.time",
:routing_key => "amqpgem.examples.services.time",
:message_id => Kernel.rand(10101010).to_s,
:reply_to => WelcomeController.replies_queue.name)
WelcomeController.replies_queue.subscribe do |metadata, payload|
puts "[response] Response for #{metadata.correlation_id}: #{payload.inspect}"
@message = payload.inspect
end
end
def self.channel
@channel ||= AMQP::Channel.new(AMQP.connection)
end
def self.replies_queue
@replies_queue ||= channel.queue("reply", :exclusive => true, :auto_delete => true)
end
end
welcome#index
異なるポートで両方のアプリケーションを起動し、ビュー
にアクセスしたとき。@message
結果がまだ返されていないため、ビューではnilです。結果は、ビューがレンダリングされてコンソールに表示されてから数ミリ秒後に到着します。
$ thin start
>> Using rack adapter
>> Thin web server (v1.5.0 codename Knife)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:3000, CTRL+C to stop
[request] Sending a request...
[response] Response for 3877031: "2012-11-27 22:04:28 -0600"
ここで驚くことでsubscribe
はありません。明らかに同期呼び出しを対象としたものではありません。驚くべきことは、AMQPgemソースコードやオンラインのドキュメントで同期の代替手段が見つからないことです。それに代わる方法で、subscribe
必要なRPC動作が得られますか?私が合法的に非同期呼び出しを使用したいシステムの他の部分があることを考えると、バニージェムはその仕事に適したツールのようには思えませんでした。別の見方をする必要がありますか?
サムストークスに応じて編集
:async/async.callbackをスローするためのポインターを提供してくれたSamに感謝します。私はこれまでこのテクニックを見たことがなく、これはまさに私が最初にこの実験で学ぼうとしていたようなものです。send_response.finish
Rails 3ではなくなりましたが、マイナーな変更を加えるだけで、少なくとも1つのリクエストに対して彼の例を機能させることができました。
render :text => @message
rendered_response = response.prepare!
後続のリクエストは。で失敗し!! Unexpected error while processing request: deadlock; recursive locking
ます。これは、SamがActionControllerで同時リクエストを許可することについてのコメントで得ていたものかもしれませんが、引用された要点はRails 2でのみ機能します。development.rbを追加するconfig.allow_concurrency = true
と、Rails 3でこのエラーがなくなりますがThis queue already has default consumer.
、AMQPから発生します。
このヤクは十分に剃られていると思います。;-)
興味深いことですが、これは単純なRPCでは明らかにやり過ぎです。このSinatraストリーミングの例のようなものは、クライアントと応答をやり取りするためのより適切なユースケースのようです。Tenderloveには、 AMQPで動作する可能性のあるRails4でイベントをストリーミングする今後の方法についてのブログ投稿もあります。
サムがHTTPの代替案についての議論で指摘しているように、REST / HTTPは、2つのRailsアプリを含む私のシステムのRPC部分にとって完全に理にかなっています。Clojureアプリへのより古典的な非同期イベント公開を含むシステムの他の部分があります。これらの場合、Railsアプリはファイアアンドフォーゲット方式でイベントを公開するだけでよいため、AMQPは、応答キューなしで元のコードを使用してそこで正常に機能します。