EIP / Structuring Channels


Case Study: Bond Pricing System

Structuring Channels

  • パターンを使うときのコツは、どのパターンをいつ使うかを知るだけではなく、もっとも効率的に使う方法を知ることである。各パターンを実装するときには技術プラットフォームの詳細をはじめ、その他の設計の規範についても考慮しなければならない。本節では、マーケットデータサーバーが分析エンジンとやりとりするというコンテキストにおけるPublish-Subscribe Channel(106)の最も効率的な利用方法を見出すために、同じ発見プロセスをたどる。
  • リアルタイムのマーケットデータのフィードはC++のサーバーで、TIB上のマーケットデータをブロードキャストする。このデータフィードは価格を公開する各ボンド★に対して、別々のPublish-Subscribe Channelを使う。これは若干極端かもしれない。というのも、新しいボンドが増えるたびに新しいチャネルが必要になるからだ。しかし、TIBCO内で実際に新しいチャネルを作らなくてもよいので、それほど大変ではない。チャネルは、「サブジェクト」と呼ばれる階層的なトピック名によって参照される。そして、TIBCOサーバーはサブジェクトによって特定のメッセージフローを抽出し、各サブジェクトを単一の仮想チャネルに送り込む。この結果非常に軽量なメッセージチャネルができる。
  • パブリッシャーがいくつかのチャネルに対してパブリッシュを行い、サブスクライバは関心がある価格だけをリッスンするというシステムを作ることもできたかもしれないが、そのためには、サブスクライバがMessage Filter(237)かSelective Consumer(515) をつかって、全データフローをフィルタリングして処理するべきかを判断しなければならなくなる。マーケットデータが各ボンドに向けて公開されるので、サブスクライバは一連のボンドに対して更新を登録できる★。サブスクライバが効率的に「抽出」できるようにするために、チャネルに対して選択的にサブスクライブし、メッセージを受け取ってから判断するのではなく、必要な更新だけを受け取れるようにするのだ。念のため言っておくと、フィルタリングを避けるために複数のチャネルを立てるのはあまり一般的ではない。TIBCOを使う場合は、チャネルを大量に使うよりは、独自のフィルターを実装するか、TIBCOに組み込まれたチャネルフィルタリングを活用するかを考えた方がいい★。
  • 次に設計すべきコンポーネントは分析エンジンだ。マーケットデータを修正して、TIBに再送信する。Java/JMS開発のスコープ外ではあるが、C++と密接に協力して設計した。分析エンジンを一番使うのは我々だからだ。当面の問題は、変更されたマーケットデータを再送信するためにもっとも効率的なチャネル構造を考えることだ。
  • マーケットデータ用の価格フィードを受け取る専用のMessage Chanel(60)はボンドごとにあるので、それごとに修正されたマーケットデータを送信するのがよさそうだが、ボンドの価格を変更する分析はトレーダーごとに行われるため、これではうまくいかない。変更されたデータをボンドごとのMessage Channel(60)に再送信したら、汎用的なマーケットデータをトレーダーごとのデータに置き換えて、データを破壊してしまう。しかし、トレーダーごとのマーケットデータには異なる型を用いることで、サブスクライバがメッセージを識別してデータ整合性を壊さないようにすることもできたが、それではクライアントがほかのトレーダーのメッセージを仕分けるためのフィルターを実装しなければならなくなる。加えて、サブスクライバが受信するメッセージの量も増えてしまう。
  • 選択肢は二つだ
  1. トレーダーごとにチャネルを一つ
  2. トレーダー、ボンドごとにチャネルを一つ
  • それぞれ、メリットデメリットがある。Message Channel(60)の数が肥大する。最悪ボンド×トレーダーになる。しかし、トレーダーは20くらいで、それぞれボンドの数は200を超えない。しtがって上限は10,000を下回り、そうであればマーケットデータプライスフィードが100,000ものMessage Channelを使うことを思えば大したことはない。また、TIBを使うので、Message Channelはコストが低く、数は大した問題ではない。しかし、数が増えると管理面で問題になるかもしれない。ボンドが増えるたびに書くトレーダーのチャネルをメンテしなければならない。動きの激しいシステムでは大変なことになりかねない。しかし、このシステムではそのあたりは変化しない。しかも、自動的にMessage Channelを管理してくれるインフラもある。類似のアプローチを使うレガシーコンポーネントのアーキテクチャを踏襲していることもあって、欠点は最小限におさえられる。だからといって、不必要にMessage Channelの数を増やしていいということではない。理由があれば、Message Channelが多いアーキテクチャにしてもよいということだ。
  • そして、今回の場合はロジックの実装箇所に関連して理由がある。トレーダーごとのアプローチにした場合、分析エンジンは入出力チャネルをグループ化するロジックが必要になる。これは、分析エンジンからの入力チャネルがボンド単位であるのに対して、出力はトレーダー単位になるからであって、分析エンジンは複数のボンドからの入力をすべて検査して、特定のトレーダーを探し、トレーダーごとのMessage Channelに出力する。そのため、分析エンジンはContent-Based Router(230)になっている。
  • Message Bus(137)構造に従うと、分析エンジンは汎用サーバーで、システム内のいくつかのシステムから使われる可能性がある。そのため、システム固有のロジックを詰め込みすぎるのは好ましくない。一方、トレーダーごとに分析を出力するという感会え方は企業で受け入れられているプラクティスなので、ボンドごとのアプローチはうまくいく。このボンドごとのアプローチはマーケットデータフィードごとにMessage Channelを分離しておくという考え方に影響を与えない代わりに、Message Channelをいくつか追加する。クライアントに届く前に、Content-Based Router(230)を使って、いくつかのチャネルをまとめて管理できる程度の数に抑える。トレーダーのデスクトップで動くクライアントアプリケーションが、何千何万のMessage Channelをリッスンするのは好ましくない。ここまでくると、問題は、Content-Based Router(230)をどこに置くかだ。単純にC++/TIB Channel Adapter(127)をMessage Channel(60)のゲートウェイの前に立てる★こともできるが、二つの理由から好ましくない。C++とJavaで業務ロジックが散逸してしまうし、データフローの後ろでフィルタリングするというメリットを失うことにもなってしまう。Javaコンポーネントに目を向けると、価格ゲートウェイの中に置くか、ゲートウェイとクライアントとの間に媒介コンポーネントを作るかだ。
  • 理論的には、Message Channelをボンドごとに分離すると言う考え方をクライアントにまで適応するなら、価格ゲートウェイや分析エンジンと同じチャネル構造で価格情報を再送信することもできる。こうすると、ボンドに割り当てられたTIBチャネルをすべてJMS上で複製することになる。価格ゲートウェイとクライアント間に媒介コンポーネントを作るとしても、やはり価格ゲートウェイはJMS内のチャネルをすべて複製しなければならない。しかし、価格ゲートウェイに直接ロジックを実装すれば、JMS内の大量のチャネルを複製しなくてよくなる。価格ゲートウェイは、C++/TIB Channel Adaptr(127)を通じて全トレーダーのコンシューマーとして登録する。そして、価格ゲートウェイは特定のクライアントに対して、その特定のトレーダーに関連したメッセージだけをフォワードする。こうすれば、JMS側でのMessage Channelの数を抑えつつ、TIB側での分離のメリットを最大化できる。
  • Message Channel(60) レイアウトについての議論は、パターンを統合することの重要性を示すよい例だ。目的は、Message Channel(60)を効率的に使う方法を明らかにすることだった。パターンを使うと言うだけでは不十分だ。最善の実装と、自分のシステムと統合して目の前の問題を解決する方法を探らなければならない。加えて、この例は、ビジネスのフォースも示している。度のコンポーネントにビジネスロジックを実装してもよかったなら、トレーダーごとのアプローチを採用して、全体としてチャネルの数も少なく、シンプルにしていただろう。

Selecting a Message Channel

  • Java/JMSコンポーネントとC++/TIBCOコンポーネントとの間の仕組みはわかったし、Message Channelの構造についても見てきた。次はどのタイプのJMS Message Channelを使うべきかを決めなければならない。JMSで使えるMessage Channelの中から選ぶ前に、システムのメッセージフローの概要を見ておこう。クライアントとコミュニケーションするゲートウェイは二つある(価格とコントリビューション★)。マーケットデータは価格ゲートウェイからクライアントに送られ、コントリビューションゲートウェイに出力される★。クライアントアプリケーションは、価格ゲートウェイにメッセージを送信して、各ボンドに適応される分析を変更する。コントリビューションゲートウェイも、クライアントアプリケーションにメッセージを送信し、別のtrading venuesへ価格の更新情報を伝える。
  • JMSの仕様は、二種類のMessage Channelのタイプを定義している。キュー(Point-to-Point Channel(103))とトピック(Publish-Subscribe Channel(106))だ。パブリッシュ-サブスクライブを使うのは、望むコンシューマーすべてにメッセージを送信する場合で、ポイントツーポイントを使うのは、特定のコンシューマーが特定のメッセージだけを受け取る場合だったことを思い出しておこう。
  • 多くのシステムは、クライアントアプリケーションすべてにメッセージをブロードキャストするだけだ。特定のメッセージを処理するかどうかは、各クライアントにゆだねるのである。しかし、今回の場合にはうまくいかない。大量のマーケットデータメッセージが送信されてしまうからだ。
  • Point-to-Point Channels がよさそうだ。クライアントはメッセージを単一のサーバーに送り、逆もまたしかり。しかし、トレーダーは複数のマシンに同時にログインできるという業務要件がある。二つのワークステーションに同時にログインし、ポイントツーポイントの更新が送信されたら、二つのアプリケーションのうち片方しかそのメッセージを受け取れない。Point-to-Point Channelでは、特定のメッセージを受け取れないからだ。一番最初にメッセージを受け取ったアプリケーションだけ、ということになる。
  • この問題は、Recipient List(249)を使って解決することもできる。意図された受け手のリストに対してメッセージを送信し、そのリストにはいていない受け手には届かないことを保証するのだ。このパターンを使えば、トレーダーに紐づくクライアントアプリケーションをリストに登録することになる。特定のトレーダーにメッセージを送信するときには、リスト内にある各アプリケーションに送信することになるということだ。このアプローチの欠点は、リストを管理し、メッセージを送信するために大量の実装ロジックが必要になることだ。
  • ポイントツーポイントを動くようにしたとしても、もっと良い方法がないか考えてみよう。Publish-Subscribe Channelを使えば、システムはトレーダーのアプリケーションではなく、チャネルに対してメッセージを送信できる。こうすれば、特定のトレーダー向けのメッセージを処理するクライアントアプリケーションはすべて、メッセージを受け取って処理ができる。
  • Publish-Subscribe Channelを用いる際の欠点は、サーバーコンポーネントで、単一のメッセージを処理することが保証できないことだ。サーバーコンポーネントが複数立てられ、それぞれが同じメッセージを処理して、間違った価格を送信してしまうかもしれない。
  • システムメッセージフローを思い返せば、各Message Channelはメッセージの送信が一方通行であることを保証される。サーバー-クライアントの通信をパブリッシューサブスクライブにしたいし、クライアントーサーバーの通信はポイントツーポイントにしたい。各方向で同じMessage Channelを使う必要はないので、それぞれ別々にすればよい。クライアントからサーバーへの通信はポイントツーポイントで実装し、サーバーからクライアントへの通信はパブリッシュ・サブスクライブで実装するのだ。Message Channelのこの組み合わせを使えば、ポイントツーポイントでサーバーと直接通信するという恩恵を受けつつ、パブリッシュサブスクライブのマルチキャストという性質を活かすことができる。

担当者のつぶやき

みんなの突っ込み