Observerパターンのメッセージング版。
Messagingを使っているアプリケーションで、イベントを通知したい。
そのイベントにかかわるすべての受信者にイベントをブロードキャストするには?
ブロードキャスト機能を実装するパターンとしては、GoFのObserverパターンが有名。POSAのPublisher-Subscriberパターンはそれを拡張したもので、イベントチャネルという概念が追加された。
理論上はそういうことだが、それをメッセージングの世界に適用するとどうなるだろうか。イベントはMessageとしてとりまとめられているので、Observer(Subscriber)に確実に渡せる。イベントチャネルにあたるのはMessage Channelだ。でも、Message ChannelがSubscriberへ確実にイベントを配送するにはどうすればいい?
Subscriberはイベントの通知を受け取る必要があるが、同じイベントの通知を何度も受け取るようではいけない。すべてのSubscriberが通知を受け取って、はじめてそのイベントが完了したことになる。完了したイベントは、チャネルから消さないといけない。
Subscriberどうしで示し合わせて同じタイミングでイベントを受け取るなんてことをすると、せっかくObserverパターンで結合を切り離したのが台無し。
イベントをPublish-Subscribe Channelに送る。そしてPublish-Subscribe Channelがそのコピーを各受信者に配送する。
ひとつの入力チャネルがあって、出力は(subscriberごとに)複数のチャネルに分割する。あるイベントがチャネルに配信されると、そのメッセージのコピーを各出力チャネルに配送する。チャネルの出力側はそれぞれひとつづつのsubscriberを持ち、メッセージを一度だけ受け取れるようになっている。それを使って各subscriberが一度だけメッセージを受け取り、受け取り済みのコピーはチャネルから消えるようにする。
あと、このパターンはデバッグツールとしても有用だ。たとえメッセージの受信者がひとつだけであるときでも、敢えてこのパターンを使ってもいい。そうすれば、既存のメッセージの流れを乱さずにメッセージチャネルを傍受できるようになる。チャネル上のすべてのトラフィックを監視できるので、わざわざアプリ側にprint文を埋め込んだりせずに済む。
しかし、Publish-Subscribe Channel上で盗み聞きできるという事実は弱点にもなる。給与システムと会計システムの間で給与データを送信するようなメッセージングソリューションがあったとしよう。ちょっとしたプログラムを書くだけで誰でもそのトラフィックをのぞき見できるというのは好ましくない。一方Point-to-Point Channelなら、その懸念は緩和できる。もし誰かがメッセージを横取りしたら、そこでメッセージが消えてしまうのですぐに問題を検出できるからだ。しかし、メッセージキューの実装の多くはpeek機能を提供しており、メッセージをconsumeしなくてもその中身を読めるようになっている。結局はセキュリティポリシーで縛るしかないということだ。
Event Messageの送信には、通常はこのパターンを使う。ひとつのイベントに関連する受け手が複数あることが多いからだ。もしsubscriber側での受信確認が必要ならRequest-Replyを使う。すべてのsubscriberがメッセージを受け取るまでPublish-Subscribe Channel上の各メッセージを保存しておくには、大量のストレージが必要だ。この問題を軽減するために、Publish-Subscribe Channelにメッセージを送るときにはMessage Expirationが使える。
多くのメッセージングシステムは、Publish-Subscribe Channelへのsubscriberを指定するときにワイルドカード文字を使える。ただワイルドカードを使えるのはsubscriber側だけで、publisherは常に配信先のチャネルを指定しなければならない。ベンダーによって、ワイルドカードの構文は異なる。
株式取引の話。全購読者に対して取引完了を通知したり、現在の市況データを配信したりするときにこのパターンを使える。
JMSでこのパターンを実装するにはTopicインターフェイスを実装する。
メッセージを送信するアプリケーションの例。
Topic topic = // JNDIから取得する TopicConnectionFactory factory = // JNDIから取得する TopicConnection connection = factory.createTopicConnection(); TopicSession session = connection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE); TopicPublisher publisher = session.createPublisher(topic); Message message = session.createTextMessage("The contents of the message."); publisher.publish(message);
メッセージを受信するアプリケーションの例。
Topic topic = // JNDIから取得する TopicConnectionFactory factory = // JNDIから取得する TopicConnection connection = factory.createTopicConnection(); TopicSession session = connection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE); TopicSubscriber subscriber = session.createSubscriber(topic); TextMessage message = (TextMessage) subscriber.receive(); String contents = message.getText();
MSMQ 3.0にはone-to-many messaging modelという新機能があり、二種類の手法が使える。
.NETのCLRは一対多のメッセージングモデルに直接は対応していない。しかし、COMインターフェイス経由でのアクセスはできる。
JMSを使ったサンプルを第6章に書いたので見てね。
もう少しちゃんと要約したかったけど、時間の都合でほぼ全訳垂れ流しみたいになってしまいました。