メッセージングシステムはトランザクション動作を内部的に利用している。(最初の一文そのまま)
メッセージングシステムは必然的にトランザクション動作を内部的に利用している。
どうやってクライアントはメッセージングシステムとのトランザクションを制御しているのか?
メッセージングシステムは内部でトランザクションを使わなければならない。単一の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のトランザクションのみ永続性があり、メッセージは定義上アトミックである。しかし全メッセージのトランザクションは一貫かつ独立していなければならない。メッセージはある意味チャネル内ではない。また、アプリケーションのメッセージ送受信はどんな他のスレッドやアプリケーションが同じチャネルを介して送受信しても独立されなければならない。
メッセージングシステムの内部トランザクションは、クライアントが単一のメッセージを送受信したい場合に十分かつ便利である。しかしながら、アプリケーションは様々なメッセージを調整したり他リソースとメッセージングを調整したりするために広いトランザクションが必要になる。一般的なシナリオは以下のとおり。
このようなシナリオは、単一メッセージ以上のものを含みメッセージングシステムに加えて他のトランザクショナルストアを含む、より大きなトランザクションが必要になる。トランザクションは必要であるため、もしシナリオ作品の一部(例えばメッセージを受信する)は動作するが他が動かない(データベース更新 or 他メッセージの送信)場合、全パートは何も怒らなかったかのようにロールバックされ、アプリケーションは再度実行可能になる。
まだメッセージングシステムの内部トランザクションモデルがメッセージを他メッセージまたは他リソースとの処理を調整するのには不十分である。必要なのはアプリケーションがメッセージングシステムのトランザクションを外部から制御し、メッセージングシステム内の他トランザクションまたは他と結合するための方法である。
senderとreceiverともにトランザクション可能である。senderがトランザクションをコミットするまでメッセージは実際にチャネルに追加されない。receiverもトランザクションをコミットするまでメッセージは実際にチャネルから削除されない。明示的なトランザクションを使用するsenderは、暗黙的なトランザクションを使用するreceiverを使うことがあり、その逆もある。単一のチャネルは暗黙的または明示的なトランザクション可能なsenderの組み合わせを持っているかもしれない。receiverについても同様。
トランザクション可能なreceiverにて、アプリケーションはキューからメッセージを実際に削除せずにメッセージを受け取れる。もしアプリケーションがクラッシュした場合、回復した際にメッセージはキューに残っている。アプリケーションはメッセージを受信して処理する。アプリケーションがメッセージを持って終了し、それを消費したい場合、アプリケーションはトランザクションをコミットし(成功したら)チャネルからメッセージを削除する。この時点において、もしアプリケーションがクラッシュしたら、回復した際にメッセージはチャネル上にはないので、アプリケーションはメッセージとともに完全に終了した方がよい。
メッセージングシステムのトランザクションは、アプリケーションが様々なタスクを調整するのをどうやって外部からコントロールしているのか?ここでは前述のシナリオでアプリケーションがどうなるかを見ていく。
送受信メッセージペア
メッセージグループ
メッセージ/データベース連携
メッセージ/ワークフロー連携
このように、アプリケーションは受信したメッセージを失わないようにするか、または送信すべきメッセージを送信し忘れないようにする。もし途中で何か良くないことが起こった場合、アプリケーションはトランザクションをロールバックして再試行する。
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-ReplyやPipes and Filtersのメッセージフィルタ、Message Sequence、Channel Adapterのような他パターンの一部として有用である。同様に、Event Messageのreceiverは完全にチャネルからメッセージを削除する前にイベント処理を完了できる。しかしながら、Transactional ClientsはEvent-Driven ConsumersやMessage Dispatchersとは上手く動作せず、Competing Consumers?によって問題を引き起こすが、単一のPolling Consumerとは上手く動作する。
略