Topics
Enterprise Architecture
BMTメッセージ駆動型BeanとSpringを使用したハイパフォーマンスなメッセージ処理
Pages:
1,
2,
3,
4,
5
頑強でうまく機能するとはいえ、トランザクションありのメッセージ消費モデルには、いくつかの重大な欠点があります。まず第1に、分散トランザク ションによって、処理パフォーマンスに重大な影響が出ることです(ローカルトランザクションからXAに切り替えると、パフォーマンスが50%低下するよう なケースも珍しくありません)。
第2の欠点は、CMTによるため、実際のトランザクションコミットは、
onMessage()呼び出しが戻った後に、ア プリケーションの外部で実行されることです。これは大したことではないように思えますが(結局、CMTの概念そのものが、アプリケーションをトランザク ション処理から解放することです)、不都合な点もあります。それは、トランザクションがコミットされるまで、エラー条件が検出されない場合があるからで す。たとえば、BEA WebLogic Serverの場合、デフォルトでは、CMP Beanによる処理の結果生じるすべてのDML操作は、トランザクションコミット時まで延期されます。これは、アプリケーション側が、CMP Beanインスタンスの更新に成功したと思っていても、実際には、データベース内の何らかの制約違反のためにSQL更新に失敗することがあり得ることを意 味します。最悪なことには、アプリケーションコードはその例外を捕捉できないため、アプリケーションコードで、これに対処することはできませんし、適切な ログを残すことすらできません。
DML操作の遅延に対する対策はあります。たとえば、BEA WebLogic Serverでは、デプロイメント記述子内でそれを無効にすることができます。しかし、これには、パフォーマンスの低下が伴います。J2EEサーバは、実 行の効率を上げるために、これ以上SQL更新を集約したりバッチすることはできません。また、後からトランザクションがロールバックされることになった場 合、SQL更新を完全にスキップすることはできません。
この論文では、トランザクションのライフサイクルをこれまで以上に管理しつつ、Bean管理トランザクション(BMT)を含めるアプローチによっ て、同じサービス品質を提供する方法を提案します。アプリケーションコードは、従来より遥かに明確にエラーの回復や報告ができるようになります。また、前 述のCMTモデルの欠点をすべて克服できます。さらに、メッセージ取得をトランザクションスコープから取り除いた結果、かなりのパフォーマンス向上が期待 できます。
BMTアプローチを見つける前は、このような場合、キューからのメッセージ消費に伴って何が起こるかを分析する必要がありました。BMT区分で MDBをデプロイすれば、J2EEサーバは、MDBがリスンしているJMS先(キューまたはトピック)を、トランザクションに加えなくなります(トランザ クションは、メッセージがキューから取り出された後に始まります)。この場合、BMT MDBは、デプロイメント記述子内で非XAコネクションファクトリを持つように設定されていなければなりません。さもないと、J2EEサーバはデプロイに 失敗します。
JMS仕様(JMS 1.1 セクション 4.5.2)によれば、メッセージリスナが、
AUTO_ACKNOWLEDGEモードまたは
DUPS_OK_ACKNOWLEDGEモードで、トランザクションなしでデプロイされた場合は、
RuntimeExceptionまたはそのサブクラスが、
onMessage()メソッドから送出されます。そして、メッセージが再配信されます。つまり、BMTを使用するようにユースケースを再設計することができるのです。メッセージの処理中に何か不具合が発生した場合は、アプリケーションコードが
RuntimeExceptionを送出し、メッセージが再配信(再試行)されます。このアプローチは非常にうまく機能します。なぜなら、回復不可能なエラーを示すために
RuntimeExceptionを使用するのは、とても自然だからです(たとえば、Springフレームワークの例外階層は、ほとんどすべて
RuntimeExceptionの サブクラスに基づいています)。メッセージは、一定の回数(MOMソフトウェアレベルで設定可能)まで再配信されます。その後、通常は、破棄されるか、 デッドメッセージキューに移されます。あるいは、アプリケーションコードが、メッセージが再配信された回数をカウントするようにして、再配信を停止して、 メッセージを処理せずに(適切なエラーメッセージを生成して)消費するか、別のキューに移すタイミングを決定することもできます。
このように、前述の振る舞いによって、メッセージの再配信を確実に制御できます。処理エラーが繰り返し発生した場合には、アプリケーションが再試行 を行うことができます。ここからは、BMTケースにおいて、once-and-only-onceの振る舞いをどのようにして保証するかを説明していきま す。
では、トランザクションなしのメッセージ消費モデルにおけるイベントシーケンスを見てみましょう。
onMessage()メソッドに渡されます。
onMessage()呼び出しが(
RuntimeExceptionを送出せずに)正常に終了した場合は、そのメッセージの確認応答が行われて、メッセージがキューから削除されます。
このシーケンスは、論理的には比較的単純だと思わるかもしれません。しかし、実装は、単純なCMTケースよりも少し複雑になります。その見返りに、 メッセージ処理の扱いが格段に柔軟になります。たとえば、トランザクションをコミットしたりロールバックするのは、アプリケーションコードの責任になりま す。また、それは、メッセージの確認応答とは独立です。メッセージの確認応答は、
onMessage()メソッドの呼び出しが正常に戻ってくるまでは、行われないことに注意してください。
ここまでは良いとして、トランザクションありのメッセージ消費モデルを使用した場合と同じQoS(once and only once)を保証できるかどうかを見ていきましょう。それには、次の2つのことを分析する必要があります。
上記の1番目の項目は非常に単純です。我々の議論から明らかなとおり、
onMessage()が正常に終了するまでは、メッセージの確認応答が行われないので、アプリケーションコードが、正常に終了しなかった場合の例外を見逃さず、例外が送出された場合は確実にトランザクションをロールバックすればよいのです。これは、とても自然なアプローチです。
上記の2番目の項目は、少しややこしくなります。これまで見てきたとおり、ビジネス処理の最中に例外が送出されると、メッセージが再配信されます。 前述のパラダイムに従っている場合は、何の問題もありません。例外が発生したら、トランザクションをロールバックして、J2EEサーバにその例外を再送出 します。もちろん、エラーを本当に回復できない場合には、メッセージの再配信が無限に繰り返されないようにするアクションを取る必要があります。これは、 MOMレベルで実行することもできますし(たとえば、デッドメッセージキュー、再配信の上限など)、これから示すように、アプリケーションレベルで処理す ることもできます。
直前の処理がトランザクションのロールバックで終了した場合は、メッセージの再配信に特に問題は生じません。アプリケーションの立場からは、(トラ ンザクションがロールバックされたため、同じメッセージに対する直前の処理が把握できないので)次の配信は、新規メッセージとして扱われなければなりませ ん。このため、特別な処理ルールがなくてもこのケースはうまくいきます。
他のコンポーネントに深刻な不具合がある場合に(たとえば、J2EEサーバのダウンなど)、何が起こるかを分析しておくことも重要です。再起動した 後に、要求されるQoSの見地から、システムが整合性のある状態にあるかどうかです。(一番最後のステップである)メッセージの確認応答の前に不具合が発 生すると、メッセージの再配信が行われます。したがって、注意しなければならない唯一の障害シナリオは、コードが正常に処理を終了してBMTトランザク ションがコミットされた 後で、J2EEサーバによってメッセージの確認応答が行われる 前に、J2EEサーバ、 MOM、またはそれらの間の接続に不具合が生じた場合です。このような場合、システムは不整合状態になります。アプリケーション側からは、メッセージが正 常に処理されているにもかかわらず、MOM側からは、メッセージが正常に配信されていない(不具合のために確認応答が行われていない)ことになるからで す。その結果、メッセージが重複して配信処理され、QoS契約に違反することになります。トランザクションコミットと確認応答の間隔は非常に短いので、お そらく、このような故障シナリオは、それほど頻繁には発生しないはずです。それでも、システムへの負荷が非常に大きい場合、何百ものメッセージを同時に処 理できなければ、このような事態が発生する可能性は高くなります。このため、回復メカニズムを構築する際には、このようなシナリオも考慮する必要がありま す。
我々は、最も厳しいQoSレベル(once and only once)に焦点を当てているために、このような珍しいケースにおける重複からの回復を考慮する必要があるのだということを再度強調しておきます。もっと ゆるやかなQoSレベル(たとえば、once or more)でユースケースを開発する場合は、我々が説明しようとしているこの回復メカニズムは、読み飛ばしてかまいません。
まとめると、BMTによるトランザクションなしのメッセージ取得を使用しているときに、once-and-only-once のQoSを保証するには、メッセージの再配信動作(EJB仕様のとおりに、
onMessage()メソッドから
RuntimeExceptionが送出されることによって実行される)を制御する必要があります。また、特定のメッセージ処理ステージ(
onMessage()呼び出しからの戻りとメッセージの確認応答の間)の最中に故障が発生した場合に、メッセージの重複処理を防止するメカニズムを備える必要があります。次に、メッセージの重複処理の防止について検討していきましょう。