HellWorldサンプル

ap4r-0.2.0をリリースしました。

  • [ANN] ap4r-0.2.0 Released

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/220383

前回のエントリでも触れていますが、新しいバージョンで追加された機能は、

  • 非同期連携プロトコルの拡充
  • SAF(Store and forward)による信頼性の向上

となります。今回はこれらの機能が実際にどのように使われるのかをサンプルを通じて紹介したいと思います。サンプルはgem同様、RubyForgeにて公開しています。DLはこちらから。


HelloWorldサンプルは単純なファイル書き出しアプリ(書き出す文字が"HelloWorld")で、同期処理(SyncHelloControllerの各アクション)と非同期処理(AsyncWorldControllerの各アクション)で構成されています。基本的な処理の流れは、以下のとおりです。

  • Rails上での同期処理
    • クライアントからのアクション呼び出し
    • ファイル書き出し
    • キュー(reliable-msg)に後続処理のためのメッセージを突っ込む
  • AP4Rプロセスによる呼び出し
    • キューからメッセージを取り出す
    • 指定の宛先にメッセージを送信(ここのプロトコルXML-RPC or SOAP or 生のHTTP)
  • Rails上での非同期処理
    • アクション呼び出し
    • (sleep 10)
    • ファイル書き出し


AP4Rからの後続処理の呼び出しプロトコルによって同期処理部分のアクションは以下のようになります。いずれも後続処理用のパラメータを作成し、async_dispatchメソッドに渡します(このメソッドのなかでキューへの突っ込みが行われます)。

  def execute

    open( 'HelloWorld.txt', 'w' ) do |f|
      f.puts "Hello # ...written at #{Time.now.to_s}";
    end

    req = WorldRequest.new(:world_id => 1, :message => "World")
    async_dispatch(req,
                   {:controller => 'async_world', :action => 'execute'},
                   {:dispatch_mode => :XMLRPC} )

    render :action => 'response'

  end
  def execute_via_soap
    open( 'HelloWorld.txt', 'w' ) do |f|
      f.puts "Hello # ...written at #{Time.now.to_s}";
    end

    req = WorldRequest.new(:world_id => 1, :message => "World")
    async_dispatch(req,
                   {:controller => 'async_world', :action => 'execute'},
                   {:dispatch_mode => :SOAP} )

    render :action => 'response'
  end
  • HTTP
  def execute_via_http
    open( 'HelloWorld.txt', 'w' ) do |f|
      f.puts "Hello # ...written at #{Time.now.to_s}";
    end

    req = {:world_id => rand(100), :message => "World"}
    async_dispatch(req,
                   {:controller => 'async_world', :action => 'execute_via_http'},
                   {:dispatch_mode => :HTTP} )

    render :action => 'response'
  end


どのプロトコルでもだいたい似たような実装となるようにしています。注意する点としては、XML-RPCSOAPを利用した場合にはパラメータとしてActionWebService::Structを継承したクラスが必要になり、またサービスのスケルトンクラスが必要になることです。

サンプルではそれぞれ以下のようなクラスがあります。

  class WorldRequest < ActionWebService::Struct
    member :world_id, :int
    member :message, :string
  end
  class AsyncWorldApi < ActionWebService::API::Base
    api_method :execute,
               :expects => [{:request => WorldRequest}],
               :returns => [:bool]
  end

生のHTTPを利用した場合には、こうしたクラスは必要ないのでアプリケーションの開発者としては同期/非同期処理の連携時にとくに気を遣う必要がなくなります♪



ちなみにSAFを利用する場合には、上記処理をtransaction_with_safメソッドのブロックに書くだけになります。こうすると、async_dispatchメソッドの中でsotreし、ブロックを抜けたところでforward(実際にキューに突っ込む)するようになります。
シンプルだな〜と感じてもらえるとよいのですが... (^^;

  • SAFを利用(at-least-once なQoSレベルとなりメッセージをロストしません)
  def execute_with_saf
    @delete_mode = :logical  # another option is :physical.

    transaction_with_saf do
      open( 'HelloWorld.txt', 'w' ) do |f|
        f.puts "Hello # ...written at #{Time.now.to_s}";
      end

      req = WorldRequest.new(:world_id => 1, :message => "World")
      async_dispatch(req,
                     {:controller => 'async_world', :action => 'execute'},
                     {:dispatch_mode => :XMLRPC} )

      render :action => 'response'
    end
  end


興味が湧いたらぜひ触ってみてください! よろしくお願いします。