AP4R,Rubyで非同期メッセージング

第3回 SAF機能とテストサポートによる安心非同期

この記事を読むのに必要な時間:およそ 10 分

SAF機能を利用してみましょう

前回非同期化したアプリケーションをベースに拡張します。Rails,AP4Rプロセスが1つずつの構成です。URL変換フィルタの機能は利用しませんので,config/queues_mysql.cfgの modify_rules の設定はコメントアウトしておいてください。

ベースとなるサンプルの一連のコードは以下からも取得できます。

% svn checkout http://ap4r.rubyforge.org/svn/tags/200709_gihyo_async_shop

準備

本題にはいる前にAP4Rのアップデートをしましょう。最新バージョンのap4r-0.3.3がリリースされています。テストサポート機能はこのリリースから含まれていますので,これからご紹介する内容を試すためには必須となります。

% gem update ap4r

また,async_shop/as_rails/vendor/plugins以下のプラグインも上書きします。as_rails/vendor/plugins/ap4r以下をいったん消したのち,以下のコマンドを実行してください。インターネット環境に接続できない場合は,RubyGemsでインストールされた AP4Rパッケージのgems/ap4r-0.3.3/rails_plugin/以下をコピーしてご利用ください。

% ruby script/plugin install http://ap4r.rubyforge.org/svn/tags/ap4r-0.3.3/samples/HelloWorld/vendor/plugins/ap4r

SAF用テーブルの準備

SAF機能でメッセージを保管するテーブルを用意します。すでにordersテーブルとpaymentsテーブルの作成に使ったmigrationファイルがあるので,以下のような結果になると思います。

% cd as_rails
% ruby script/generate migration create_table_for_saf
     exists  db/migrate
     create  db/migrate/003_create_table_for_saf.rb

db/migrate/003_create_table_for_saf.rbを以下のように編集します。

class CreateTableForSaf < ActiveRecord::Migration
  def self.up
    create_table :stored_messages do |t|
      t.column :duplication_check_id, :string, :null => false
      t.column :queue, :string, :null => false
      t.column :headers, :binary, :null => false
      t.column :object, :binary, :null => false
      t.column :status, :integer, :null => false
      t.column :created_at, :datetime, :null => false
      t.column :updated_at, :datetime, :null => false
    end
  end

  def self.down
    drop_table :stored_messages
  end
end

RubyGemsでインストールされたAP4Rのなかにもmigrationファイルのサンプルがあります(gems/ap4r-0.3.3/lib/ap4r/xxx_create_table_for_saf.rb⁠⁠ 。xxxの部分を003に変えてご利用ください。

ap4r.transaction

準備は整ったので,さっそくコードの拡張にはいりましょう。変更するのは,app/controllers/async_shop_controller.rbです。以下に修正前のものと修正後のものを並べます。

修正前
class AsyncShopController < ApplicationController
  (省略)
  def order
    begin
      Order.transaction do
        @order = Order.new(params[:order])
        @order.save!

        ap4r.async_to({:action => 'payment'},
                      {:order_id => @order.id})

        flash[:notice] = 'Order was successfully created.'
        redirect_to :action => 'index'
      end
    rescue Exception
      flash[:notice] = 'Order was failed.'
      render :action => 'order_form'
    end
  end
  (省略)
end
修正後
class AsyncShopController < ApplicationController
  (省略)
  def order
    begin
      ap4r.transaction do
        @order = Order.new(params[:order])
        @order.save!

        ap4r.async_to({:action => 'payment'},
                      {:order_id => @order.id})

        flash[:notice] = 'Order was successfully created.'
        redirect_to :action => 'index'
      end
    rescue Exception
      flash[:notice] = 'Order was failed.'
      render :action => 'order_form'
    end
  end
  (省略)
end

違いに気づいていただけたでしょうか?

  def order
    begin
      Order.transaction do

  def order
    begin
      ap4r.transaction do

に変わっているだけです。

修正は以上ですが,せっかくなので ap4r.transaction メソッドの動きをもう少しみてみましょう。

図3 ap4r.transaction の裏側

図3 ap4r.transaction の裏側

上の図から分かるように,処理は2段階で行なわれます。まず,ブロック内の ap4r.async_to メソッドの部分では,アプリケーションによる業務DBへのCRUDと同様にSAF用テーブルにメッセージが保管されます。SAF用のテーブルはさきほどmigrationにて業務DB上に準備しました。したがって,トランザクションはアプリケーションのCRUDと一緒になります。ここがSAF(Store and Forward)のStoreにあたります。

次に,ブロックを抜けたところで業務DBがコミットされます。これで,業務DBへのCRUDの情報とともに,非同期連携用に作成されたメッセージの情報もいったん業務DB内に永続化されます。そして,そのメッセージの情報をAP4Rサービスに送信します。ここが,Forwardにあたります。

こうして,業務DBのCRUDとメッセージの生成がアトミックに処理されることになり,信頼性の高いメッセージングが実現されます。

実行

実行してみましょう。RailsとAP4Rを起動してください。

% cd as_rails
% ruby script/server
% cd as_ap4r
% ruby script/mongrel_ap4r start -A config/queues_mysql.cfg

以下のURLにアクセスします。

「New order」リンクから注文フォーム画面を開き,適当なアイテム名を入力して「Order」ボタンで注文します。注文が成功した場合は,注文結果のリストが表示されます。第2回までの作業で,⁠重い」決済処理を注文処理から分離していますので,決済日時(Payed at)の項目が「Not yet.」として表示されるでしょう。タイミングによっては,ユーザーからの注文結果表示のリダイレクト処理より先に,AP4Rからの決済処理のリクエストが実行されてしまい,注文結果の画面がすぐに開かないかもしれません。その場合は,リバースプロキシを利用した構成や,第2回の最後に紹介した,非同期処理を別プロセスに送信する構成をお試しください。

無事に注文結果が表示されたでしょうか?

ユーザーから見た動きは,第2回で非同期拡張したアプリケーションから変わっていませんね。

著者プロフィール

加藤究(かとうきわむ)

フューチャーアーキテクト株式会社,シニアコンサルタント。土木専攻だった大学時代には,道路や橋の建設について学んできたが,社会に出てからは Javaのメッセージングミドルウェアの開発/保守などに従事。昨年,Rubyで書いたメッセージングライブラリ,AP4Rでオーンソースの世界に仲間入り。現在は,AP4Rの開発/導入サポート中。

URLhttp://d.hatena.ne.jp/kiwamu/


篠原俊一(しのはらしゅんいち)

フューチャーアーキテクト株式会社,シニアコンサルタント。大学時代は物理学を専攻,素粒子論を研究,10次元の世界に住んでいた。入社後まもなく,Martin FowlerのblikiでRubyを知り,"The Ruby Way"に学ぶ。 Rubyとオープンソースが大好きなプログラマとして,AP4Rを開発中。

URLhttp://d.hatena.ne.jp/ita-wasa/