EIP / Transactional Client


Transactional Client

一言要約

メッセージングシステムはトランザクション動作を内部的に利用している。(最初の一文そのまま)

要約

メッセージングシステムは必然的にトランザクション動作を内部的に利用している。

どうやってクライアントはメッセージングシステムとのトランザクションを制御しているのか?

メッセージングシステムは内部でトランザクションを使わなければならない。単一のMessage Channelは、複数のsenderとreceiverを持つことができるので、メッセージングシステムはsenderが互いのMessagesを上書きしないよう、複数のPoint-to-Point Channelのreceiverは同じメッセージを受け取らないよう、複数のPublish-Subscribe Channelのreceiverは互いにメッセージのコピーを受け取るよう、メッセージを調和しなければならない。この全てを管理するには、メッセージングシステムは内部的にメッセージがチャネルに追加されているか or されてないか、チャネルから読めるか or 読めないかを確認するために、トランザクションを内部的に利用している。メッセージングシステムはまた、senderのコンピュータからreceiverのコンピュータにメッセージをコピーし、任意の時点でメッセージがどこかのコンピュータ上にのみ「実際に」あるよう、トランザクション(できるならば2フェーズの分散トランザクション)を利用しなければならない。

メッセージを送受信するMessage Endpointsは(気づいてなくとも)トランザクショナルである。メッセージをチャネルに追加する送信メソッドは、同時にチャネルから追加/削除される他メッセージから隔離するためにトランザクション内で行われる。同様に、受信メソッドもトランザクションを使っており、他のpoint-to-pointの受信者から同じメッセージを得ないようにし、pub-subの受信者が同じメッセージを2回読まないことを保証する。

トランザクションはしばしばACID(原子性、一貫性、独立性、永続性)であると言われる。Guaranteed Messagingのトランザクションのみ永続性があり、メッセージは定義上アトミックである。しかし全メッセージのトランザクションは一貫かつ独立していなければならない。メッセージはある意味チャネル内ではない。また、アプリケーションのメッセージ送受信はどんな他のスレッドやアプリケーションが同じチャネルを介して送受信しても独立されなければならない。

メッセージングシステムの内部トランザクションは、クライアントが単一のメッセージを送受信したい場合に十分かつ便利である。しかしながら、アプリケーションは様々なメッセージを調整したり他リソースとメッセージングを調整したりするために広いトランザクションが必要になる。一般的なシナリオは以下のとおり。

送受信メッセージペア
Request-Replyのようなシナリオ、あるいはMessage RouterMessage Translatorのようなメッセージフィルタを実装する時に1つのメッセージを受信し、別を送信する
メッセージグループ
Message Sequenceのような関連メッセージのグループを送受信する
メッセージ/データベース連携
Channel Adapterのようなメッセージ送受信とデータベース更新の組み合わせ。例えば、アプリケーションが商品注文にメッセージを受信し処理する時、アプリケーションは商品在庫データベースも更新する必要がある。同様に、Document Messageのsenderは正常に送信された時のみ、永続ドキュメントを削除したい。receiverはメッセージが本当に消費されたとみなされるまでは永続させたい。
メッセージ/ワークフロー調整
作業項目を実行するためRequest-Replyメッセージのペアを使う。リクエストが送信されない限り作業項目が得られず、返答が受け取れない限り作業項目は完了または中断しない。

このようなシナリオは、単一メッセージ以上のものを含みメッセージングシステムに加えて他のトランザクショナルストアを含む、より大きなトランザクションが必要になる。トランザクションは必要であるため、もしシナリオ作品の一部(例えばメッセージを受信する)は動作するが他が動かない(データベース更新 or 他メッセージの送信)場合、全パートは何も怒らなかったかのようにロールバックされ、アプリケーションは再度実行可能になる。

まだメッセージングシステムの内部トランザクションモデルがメッセージを他メッセージまたは他リソースとの処理を調整するのには不十分である。必要なのはアプリケーションがメッセージングシステムのトランザクションを外部から制御し、メッセージングシステム内の他トランザクションまたは他と結合するための方法である。


Transactional Clientを使う
メッセージングシステムを持つクライアントセッションをトランザクション可能にするので、クライアントはトランザクション境界を特定できる。
TransactionalClientSolution.gif

senderとreceiverともにトランザクション可能である。senderがトランザクションをコミットするまでメッセージは実際にチャネルに追加されない。receiverもトランザクションをコミットするまでメッセージは実際にチャネルから削除されない。明示的なトランザクションを使用するsenderは、暗黙的なトランザクションを使用するreceiverを使うことがあり、その逆もある。単一のチャネルは暗黙的または明示的なトランザクション可能なsenderの組み合わせを持っているかもしれない。receiverについても同様。

トランザクション可能な受信シーケンス

トランザクション可能なreceiverにて、アプリケーションはキューからメッセージを実際に削除せずにメッセージを受け取れる。もしアプリケーションがクラッシュした場合、回復した際にメッセージはキューに残っている。アプリケーションはメッセージを受信して処理する。アプリケーションがメッセージを持って終了し、それを消費したい場合、アプリケーションはトランザクションをコミットし(成功したら)チャネルからメッセージを削除する。この時点において、もしアプリケーションがクラッシュしたら、回復した際にメッセージはチャネル上にはないので、アプリケーションはメッセージとともに完全に終了した方がよい。

メッセージングシステムのトランザクションは、アプリケーションが様々なタスクを調整するのをどうやって外部からコントロールしているのか?ここでは前述のシナリオでアプリケーションがどうなるかを見ていく。

送受信メッセージペア

何をするか
トランザクションを開始し、最初のメッセージを受け取って処理し、二番目のメッセージを作成して送信してコミットする。(この動作はRequest-ReplyMessage Router>EIP?Message Translatorの一部として実装されている)
行っていること
二番目のメッセージが正常にチャネルに追加されるまでは、最初のメッセージがチャネルから削除されるのを引き留める。
トランザクションの種類
2つのメッセージが同じメッセージングシステム内のチャネルを介して送信される場合、2つのチャネルを包含するトランザクションはシンプルなものである。しかしながら、Messaging Bridgeのようにもし2つのチャネルが別々のメッセージングシステムによって管理されているならば、トランザクションは2つのメッセージングシステムを調整する分散型になる。
警告
単一のトランザクションは、返答を送信するリクエストのreceriverのみ機能する。リクエストのsenderは、リクエストを送って返答を待つために、単一トランザクションを使えない。もしこれを行おうとした場合、リクエストは実際に送信されない(送信トランザクションはコミットされないので)。よって返答が受け取られることはない。

メッセージグループ

何をするか
トランザクションを開始し、(Message Sequenceのように)グループ内の全メッセージを送受信し、コミットする。
行っていること
送信する際、全ての送信が成功するまで、グループ内のメッセージは何もチャネルに追加されない。受信する際、全てを受け取るまでメッセージはチャネルから削除されない。
トランザクションの種類
全てのメッセージはが単一のチャネルから送信または受信され、チャネルは単一のメッセージングシステムによって管理されているので、トランザクションはたった一つ。また、多くのメッセージングシステム実装では、単一トランザクションでのメッセージグループの送信すると、送った順番でチャネルのもう一方の終端で受け取ることが保証される。

メッセージ/データベース連携

何をするか
トランザクションを開始し、メッセージを受け取り、データベースを更新し、コミットする。または、データベースを更新して他への更新をレポートするメッセージを送信し、コミットする。(この振る舞いはChannell Adapter?によって実装されていることがある)
行っていること
データベースが更新されるまでメッセージは削除されない(メッセージが送信出来ない場合はデータベース変更は行われない)。
トランザクションの種類
メッセージングシステムとデータベースはそれぞれ独自のトランザクションマネージャを持っているので、これらを調整するトランザクションは分散された一つになる。

メッセージ/ワークフロー連携

何をするか
作業項目を実行するためにRequest-Replyメッセージのペアを使う。トランザクションを開始し、作業項目を要求してリクエストメッセージを送信し、コミットする。または、もう一つのトランザクションが開始し応答メッセージを受け取り、作業項目を完了またはアボートし、コミットする。
行っていること
リクエストが送信されない限り作業項目はコミットされない。また、作業項目が更新されない限り応答は削除されない。
トランザクションの種類
メッセージングシステムとワークフローエンジンはそれぞれ独自のトランザクションマネージャを持っているので、これらを調整するトランザクションは分散された一つになる。

このように、アプリケーションは受信したメッセージを失わないようにするか、または送信すべきメッセージを送信し忘れないようにする。もし途中で何か良くないことが起こった場合、アプリケーションはトランザクションをロールバックして再試行する。

Event-Driven Consumersを使ったTransactional clientsは期待通りに動かないことがある。consumerは通常アプリケーションにメッセージを渡す前にメッセージ受信のトランザクションをコミットしなければならない。また、もしアプリケーションが検証してから決定したい場合に消費したくない。また、アプリケーションがエラーを検出して消費動作をロールバックしたい場合、トランザクションにアクセスできないので不可能である。よって、event-driven consumerはそのクライアントがトランザクション可能かどうかに関わらず同じ動作をする傾向がある。

メッセージングシステムは分散トランザクションに参加可能である(いくつかの実装はサポートしてないかもしれない)。JMSではプロバイダがXAリソースとして機能し、Java Transaction API(JTA)トランザクションに参加できる。この動作は、javax.jmsパッケージ内のXAクラス(特にjavax.jms.XASession)やjavax.transaction.xaパッケージによって定義されている。JMS仕様では、JMSクライアントは直接分散トランザクションを処理しないようすすめており、よってアプリケーションはJ2EEアプリケーションサーバによって提供された分散トランザクションサポートを使うべきである。MSMQもまたXAトランザクションに参加できる。この動作は.NETのMessageQueue.TransactionalプロパティとMessageQueueTransaction?クラスにて公開されている。

前述のように、Transactional ClientsはRequest-ReplyPipes and Filtersのメッセージフィルタ、Message SequenceChannel Adapterのような他パターンの一部として有用である。同様に、Event Messageのreceiverは完全にチャネルからメッセージを削除する前にイベント処理を完了できる。しかしながら、Transactional ClientsはEvent-Driven ConsumersMessage Dispatchersとは上手く動作せず、Competing Consumers?によって問題を引き起こすが、単一のPolling Consumerとは上手く動作する。

例:JMS トランザクションセッション


担当者のつぶやき

みんなの突っ込み