動的ルーター

Spring Integration は、一般的なコンテンツベースのルーティングのユースケースに対応する非常に少数の異なるルーター構成と、カスタムルーターを POJO として実装するオプションを提供します。例: PayloadTypeRouter は、受信メッセージのペイロード型に基づいてチャネルを計算するルーターを構成する簡単な方法を提供しますが、HeaderValueRouter は、特定のメッセージヘッダーの値を評価してチャネルを計算するルーターを構成するのと同じ便利な機能を提供します。式ベース(SpEL)のルーターもあり、式の評価に基づいてチャネルが決定されます。これらの型のルーターはすべて、いくつかの動的特性を示します。

ただし、これらのルーターはすべて静的構成を必要とします。式ベースのルーターの場合でも、式自体はルーター構成の一部として定義されます。これは、同じ値で動作する同じ式が常に同じチャネルの計算になることを意味します。このようなルートは明確に定義されており、予測可能であるため、ほとんどの場合、これで問題ありません。ただし、メッセージフローを別のチャネルにルーティングできるように、ルーター構成を動的に変更する必要がある場合があります。

例: メンテナンスのためにシステムの一部を停止し、一時的にメッセージを別のメッセージフローに再ルーティングしたい場合があります。別の例として、より具象型の java.lang.Number (PayloadTypeRouter の場合)を処理する別のルートを追加することにより、メッセージフローをより細かくしたい場合があります。

残念ながら、これらのゴールのいずれかを達成するための静的ルーター構成では、アプリケーション全体を停止し、ルーターの構成を変更(ルートを変更)し、アプリケーションを再起動する必要があります。これは明らかに誰もが望む解決策ではありません。

動的ルーター (英語) パターンは、システムまたは個々のルーターを停止することなく、ルーターを動的に変更または構成できるメカニズムを記述します。

Spring Integration が動的ルーティングをサポートする方法の詳細に入る前に、ルーターの典型的なフローを考慮する必要があります。

  1. チャネル識別子を計算します。これは、ルーターがメッセージを受信すると計算される値です。通常、これは String または実際の MessageChannel のインスタンスです。

  2. チャネル識別子をチャネル名に解決します。このセクションの後半で、このプロセスの詳細を説明します。

  3. チャネル名を実際の MessageChannel に解決します

MessageChannel はルーターのジョブの最終製品であるため、ステップ 1 の結果が MessageChannel の実際のインスタンスになる場合、動的ルーティングに関してできることはあまりありません。ただし、最初のステップの結果が MessageChannel のインスタンスではないチャネル ID になった場合、MessageChannel を導出するプロセスに影響を与える方法はかなりあります。ペイロード型のルーターの次の例を考えてみましょう。

<int:payload-type-router input-channel="routingChannel">
    <int:mapping type="java.lang.String"  channel="channel1" />
    <int:mapping type="java.lang.Integer" channel="channel2" />
</int:payload-type-router>

ペイロード型のルーターのコンテキスト内で、前述の 3 つの手順は次のように実現されます。

  1. ペイロード型の完全修飾名(たとえば、java.lang.String)であるチャネル識別子を計算します。

  2. チャネル識別子をチャネル名に解決します。この場合、前の手順の結果を使用して、mapping 要素で定義されたペイロード型マッピングから適切な値を選択します。

  3. 前のステップの結果で識別されたアプリケーションコンテキスト(できれば MessageChannel)内の Bean への参照として、チャネル名を MessageChannel の実際のインスタンスに解決します。

つまり、プロセスが完了するまで、各ステップは次のステップにフィードします。

次に、ヘッダー値ルーターの例を考えます。

<int:header-value-router input-channel="inputChannel" header-name="testHeader">
    <int:mapping value="foo" channel="fooChannel" />
    <int:mapping value="bar" channel="barChannel" />
</int:header-value-router>

これで、ヘッダー値ルーターの 3 つのステップがどのように機能するかを検討できます。

  1. header-name 属性によって識別されるヘッダーの値であるチャネル識別子を計算します。

  2. チャネル ID をチャネル名に解決します。前のステップの結果を使用して、mapping 要素で定義された一般的なマッピングから適切な値を選択します。

  3. 前のステップの結果で識別されたアプリケーションコンテキスト(できれば MessageChannel)内の Bean への参照として、チャネル名を MessageChannel の実際のインスタンスに解決します。

2 つの異なるルーター型の前述の 2 つの構成は、ほとんど同じように見えます。ただし、HeaderValueRouter の代替構成を見ると、次のように、mapping サブ要素がないことが明確にわかります。

<int:header-value-router input-channel="inputChannel" header-name="testHeader"/>

ただし、構成はまだ完全に有効です。自然な問題は、第 2 ステップのマッピングについてはどうでしょうか?

2 番目のステップはオプションになりました。mapping が定義されていない場合、最初のステップで計算されたチャネル識別子の値は自動的に channel name として扱われ、現在は 3 番目のステップのように実際の MessageChannel に解決されます。また、2 番目のステップは、チャネル識別子がチャネル名に解決する方法を変更できるプロセスを導入し、最終的な決定プロセスに影響を与えるため、ルーターに動的特性を提供するための重要なステップの 1 つであることも意味します。初期チャネル識別子からの MessageChannel のインスタンス。

例: 上記の構成では、testHeader 値が "kermit" であり、現在はチャネル識別子であると想定しています(最初のステップ)。このルーターにはマッピングがないため、このチャネル識別子をチャネル名に解決することはできず(2 番目のステップ)、このチャネル識別子はチャネル名として扱われます。ただし、マッピングがあり、値が異なる場合はどうなるでしょうか? チャネル識別子をチャネル名に解決するプロセスで新しい値を決定できない場合、チャネル識別子がチャネル名になるため、最終結果は同じままです。

残っているのは、3 番目のステップでチャネル名(「カーミット」)を、この名前で識別される MessageChannel の実際のインスタンスに解決することだけです。基本的には、提供された名前の Bean ルックアップが含まれます。これで、testHeader=kermit としてヘッダーと値のペアを含むすべてのメッセージは、Bean 名(その id)が "kermit" である MessageChannel にルーティングされます。

しかし、これらのメッセージを "simpson" チャネルにルーティングしたい場合はどうすればよいでしょうか ? 静的構成の変更は明らかに機能しますが、変更するにはシステムを停止する必要もあります。ただし、チャネル識別子マップにアクセスできる場合は、ヘッダーと値のペアが kermit=simpson になる新しいマッピングを導入することができます。これにより、2 番目のステップで 'kermit' をチャネル識別子として扱い、それを 'simpson' に解決できるようになります。チャンネル名。

PayloadTypeRouter にも同じことが当てはまります。特定のペイロード型のマッピングを再マップまたは削除できるようになりました。実際、計算された値は 2 番目のステップを経て実際の channel name に解決される可能性があるため、式ベースのルーターを含む他のすべてのルーターに適用されます。

AbstractMappingMessageRouter のサブクラスであるルーター (フレームワーク定義のルーターのほとんどを含む) は動的ルーターです。これは、channelMapping が AbstractMappingMessageRouter レベルで定義されているためです。そのマップの setter メソッドは、'setChannelMapping' および 'removeChannelMapping' メソッドとともにパブリックメソッドとして公開されています。これらを使用すると、ルーター自体への参照がある限り、実行時にルーターマッピングを変更、追加、削除できます。また、JMX (JMX サポートを参照) または Spring Integration コントロールバス ( 制御バスを参照) 機能を通じて、同じ構成オプションを公開することもできます。

チャンネル名は柔軟で便利なので、チャンネルキーに戻ります。ただし、メッセージ作成者を信頼していない場合、悪意のあるアクター(システムの知識がある)が予期しないチャネルにルーティングされるメッセージを作成する可能性があります。例: キーがルーターの入力チャンネルのチャンネル名に設定されている場合、そのようなメッセージはルーターにルーティングされ、最終的にスタックオーバーフローエラーが発生します。この機能を無効にし(channelKeyFallback プロパティを false に設定)、必要に応じてマッピングを変更することをお勧めします。

コントロールバスを使用してルーターマッピングを管理する

ルーターマッピングを管理する 1 つの方法は、コントロールバス (英語) パターンを使用することです。これは、ルーターを含む Spring Integration コンポーネントを管理および監視するためのコントロールメッセージを送信できるコントロールチャネルを公開します。

制御バスの詳細については、制御バスを参照してください。

通常、特定の管理対象コンポーネント(ルーターなど)で特定の操作を呼び出すように要求する制御メッセージを送信します。次の管理操作(メソッド)は、ルーター解決プロセスの変更に固有のものです。

  • public void setChannelMapping(String key, String channelName)channel identifier と channel name の間に新しいマッピングを追加したり、既存のマッピングを変更したりできます。

  • public void removeChannelMapping(String key): 特定のチャネルマッピングを削除して、channel identifier と channel name の関連を切断できます。

これらのメソッドは、単純な変更(単一のルートの更新、ルートの追加または削除など)に使用できることに注意してください。ただし、あるルートを削除して別のルートを追加する場合、更新はアトミックではありません。これは、ルーティングテーブルが更新間で不確定な状態になる可能性があることを意味します。バージョン 4.0 以降、コントロールバスを使用して、ルーティングテーブル全体をアトミックに更新できるようになりました。次のメソッドを使用して、これを行うことができます。

  • public Map<String, String>getChannelMappings(): 現在のマッピングを返します。

  • public void replaceChannelMappings(Properties channelMappings): マッピングを更新します。channelMappings パラメーターは Properties オブジェクトなので、これをそれぞれの IntegrationMessageHeaderAccessor.CONTROL_BUS_ARGUMENTS ヘッダーに追加する必要があることに注意してください。

Properties newMapping = new Properties();
newMapping.setProperty("foo", "bar");
newMapping.setProperty("baz", "qux");
Message<?> replaceChannelMappingsCommandMessage =
                     MessageBuilder.withPayload("'router.handler'.replaceChannelMappings")
                            .setHeader(IntegrationMessageHeaderAccessor.CONTROL_BUS_ARGUMENTS, List.of(newMapping))
                            .build();

マップをプログラムで変更する場合は、型の安全性を考慮して、setChannelMappings メソッドを使用することをお勧めします。replaceChannelMappings は、String オブジェクトではないキーまたは値を無視します。

JMX を使用してルーターマッピングを管理する

Spring の JMX サポートを使用してルーターインスタンスを公開し、お気に入りの JMX クライアント(たとえば、JConsole)を使用して、ルーターの構成を変更するための操作(メソッド)を管理することもできます。

Spring Integration の JMX サポートの詳細については、JMX サポートを参照してください。