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

第4回 システムは稼働してからがはじまり

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

DLQリカバリ

発生状況

メッセージの処理中のネットワーク障害や,メッセージを処理するアプリケーションのエラーなどで,メッセージは元のキューからDLQに移動されます。ネットワークやDBサーバの障害の場合,DLQに入ったメッセージを元のキューに戻すことで,再度処理が行われます。メッセージ内のデータがおかしいなど,アプリケーションのエラーでDLQに入った場合は,アプリケーションのロジックを変更するか,データ自体を修正してから元のキューに戻す必要があります。今回は後者については触れません。

状況の確認

AP4Rが起動中であれば,irbから確認できます。

まず,DLQに入った状況を作るために,わざとエラーを発生させてみましょう。ここでは簡単のため, AsyncShopController#payment アクションの頭で

raise "dummy exception to test DLQ"

として,エラーを発生させます。この状況でブラウザから注文処理を実行すると,ブラウザには"Order was successfully created."と表示されますが,RailsおよびAP4Rのログにはエラーが記録されます。Railsのログでは payment アクションの処理中にダミーの例外が発生したこと,それによりAP4R側のログでメッセージ処理がエラーになったことが残ります。

では,DLQの中身を見てみましょう。まず,irbを起動して,DLQへの(druby越しの)参照 dlq とキューの管理をしている ReliableMsg::QueueManager ⁠のインスタンス)への参照 qm を取得します。

% irb -rubygems -rap4r
>> dlq = ReliableMsg::Queue.new "$dlq"
=> #<ReliableMsg::Queue:0x2838360 @queue="$dlq">
>> qm = dlq.send :qm
=> #<DRb::DRbObject:0x2832b04 @ref=nil, @uri="druby://localhost:6438">

次に,DLQ(内部では,$dlq という名前になっています)の一覧を表示します。

>> y qm.list(:queue => "$dlq")
--- # 一部整形しています
- :max_deliveries: 5
  :priority: 0
  :created: 1189737438
  :target_method: POST
  :redelivery: 1
  :queue: queue.async_shop.payment
  :expires:
  :id: 56205b90-4499-012a-1774-0016cb9ad556
  :delivery: :once
  :target_url: http://localhost:3000/async_shop/payment
  :dispatch_mode: :HTTP
=> nil

ここではメッセージのヘッダー部分のみが表示されていますが,1件のメッセージがあることを確認できます。ヘッダーの中で,:queue の箇所に元のキューの名前が記録されています。

リカバリ方法

ではリカバリを行います。DLQからメッセージを抜いて,元のキューに入れるという手順になります。さきほど payment アクションに入れたダミーの例外は削除しておきましょう。

>> dlq.get{|m| dlq.put(m.object, m.headers) }
=> "5f7df210-4499-012a-1774-0016cb9ad556"

これで元のキューにメッセージが入り,dispatcherスレッドがRailsにリクエストしてpayment アクションが呼ばれます。少し待ってからブラウザをリロードすると「Payed at」に時刻が表示されるはずです。

(注)
上のスクリプトでは,一見DLQに put しているように見えますが,実際には,m.headers:queue で指定されたキューに入ります。

ここで,ちょっと横道に逸れて,リカバリしている最中にエラーが発生した場合を考えてみましょう。DLQにメッセージを1つ入れてから,それを取得したタイミングで例外を投げてみます。

>> y qm.list(:queue => "$dlq")
--- # 抜粋。一部整形
- :id: e232a830-4499-012a-1774-0016cb9ad556
=> nil
>> dlq.get{|m| raise "dummy recovery error"}
RuntimeError: dummy recovery error # 以下略

さて,DLQに入っていたメッセージはどうなっているでしょうか。

>> y qm.list(:queue => "$dlq")
--- # 抜粋。一部整形
- :id: e232a830-4499-012a-1774-0016cb9ad556
=> nil

同じ :id でメッセージが残っていることが分かります。これは,reliable-msgの機能により,ブロック付きで ReliableMsg::Queue#get を呼び出すと,⁠メッセージングの意味での)トランザクション内でブロックが実行されるためです。詳しくは,RDocを参照してください。

DLQに入ったメッセージの確認と操作について説明しました。最低限のことは可能ですが,実用のためには,考慮すべき事項がいくつかあります。

  • リカバリが必要なメッセージ数が多い場合
  • メッセージの中身が大きい場合
  • リカバリ処理の自動化(例: DLQに30分入っているメッセージを自動的にリカバリする)

今後のAP4Rの拡張には,このようなことも含めていく予定です。

著者プロフィール

加藤究(かとうきわむ)

フューチャーアーキテクト株式会社,シニアコンサルタント。土木専攻だった大学時代には,道路や橋の建設について学んできたが,社会に出てからは 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/