システムマネジメント

指標と管理

このセクションでは、Spring Integration のメトリクスをキャプチャーする方法について説明します。最近のバージョンでは、Micrometer(https://micrometer.io (英語) を参照)にさらに依存しており、将来のリリースでは Micrometer をさらに使用する予定です。

メトリクスキャプチャーの構成

バージョン 4.2 より前は、メトリクスは JMX が有効になっている場合にのみ使用可能でした。JMX サポートを参照してください。

MessageSourceMessageChannelMessageHandler メトリクスを有効にするには、<int:management/> Bean をアプリケーションコンテキスト(XML)に追加するか、@Configuration クラスの 1 つに @EnableIntegrationManagement (Java)でアノテーションを付けます。MessageSource インスタンスはカウントのみを維持し、MessageChannel インスタンスと MessageHandler インスタンスはカウントに加えて期間統計を維持します。この章で後述する MessageChannel メトリクス機能および MessageHandler メトリクス機能を参照してください。

これにより、アプリケーションコンテキストで IntegrationManagementConfigurer Bean が自動的に登録されます。このような Bean はコンテキストに 1 つだけ存在でき、<bean/> 定義を介して手動で登録する場合は、Bean 名を integrationManagementConfigurer に設定する必要があります。この Bean は、コンテキスト内のすべての Bean がインスタンス化された後、その構成を Bean に適用します。

メトリクスに加えて、メインメッセージフローでデバッグロギングを制御できます。非常に大量のアプリケーションでは、isDebugEnabled() の呼び出しでさえ、一部のロギングサブシステムでは非常に高負荷になる可能性があります。このオーバーヘッドを回避するために、このようなロギングをすべて無効にすることができます。例外ロギング(デバッグまたはその他)は、この設定の影響を受けません。

次のリストは、ロギングを制御するために使用可能なオプションを示しています。

<int:management
    default-logging-enabled="true" (1)
    default-counts-enabled="false" (2)
    default-stats-enabled="false" (3)
    counts-enabled-patterns="foo, !baz, ba*" (4)
    stats-enabled-patterns="fiz, buz" (5)
    metrics-factory="myMetricsFactory" /> (6)
@Configuration
@EnableIntegration
@EnableIntegrationManagement(
    defaultLoggingEnabled = "true", (1)
    defaultCountsEnabled = "false", (2)
    defaultStatsEnabled = "false", (3)
    countsEnabled = { "foo", "${count.patterns}" }, (4)
    statsEnabled = { "qux", "!*" }, (5)
    MetricsFactory = "myMetricsFactory") (6)
public static class ContextConfiguration {
...
}
1false に設定すると、ログシステムのカテゴリ設定に関係なく、メインメッセージフローのすべてのロギングが無効になります。デバッグログを有効にするには、"true" に設定します(ログサブシステムでも有効になっている場合)。Bean 定義で設定を明示的に構成していない場合にのみ適用されます。デフォルトは true です。
2<4> のパターンのいずれかに一致しないコンポーネントのカウントメトリクスを有効または無効にします。Bean 定義で設定を明示的に構成していない場合にのみ適用されます。デフォルトは false です。
3<5> のパターンのいずれかに一致しないコンポーネントの統計メトリクスを有効または無効にします。Bean 定義で設定を明示的に構成していない場合にのみ適用されます。デフォルトは "false" です。
4 カウントを有効にする必要がある Bean のパターンのコンマ区切りリスト。! でパターンを無効にすることができます。最初の一致(正または負)が勝ちます。まれに、! で始まる Bean 名がある場合、パターン内の ! をエスケープします。例: \!something は、!something という名前の Bean と完全に一致します。
5 統計メトリクスを有効にする必要がある Bean のパターンのコンマ区切りリスト。! で pattern \ を無効にすることができます。最初の一致(正または負)が勝ちます。まれに、! で始まる Bean 名がある場合、パターン内の ! をエスケープします。\!something は、!something という名前の Bean と確実に一致します。統計の収集は、カウントの収集を意味します。
6MetricsFactory への参照。メトリクスファクトリを参照してください。

実行時に、カウントと統計は、それぞれ MessageChannelMetricsMessageHandlerMetricsMessageSourceMetrics を返す getChannelMetricsgetHandlerMetricsgetSourceMetrics (すべて IntegrationManagementConfigurer クラスから)を呼び出すことによって取得できます。

これらのクラスの詳細については、Javadoc を参照してください。

JMX が有効な場合(JMX サポートを参照)、IntegrationMBeanExporter はこれらのメトリクスも公開します。

IMPORTANT: defaultLoggingEnableddefaultCountsEnableddefaultStatsEnabled は、Bean 定義で対応する設定を明示的に構成していない場合にのみ適用されます。

バージョン 5.0.2 以降、フレームワークは、アプリケーションコンテキストに単一の MetricsFactory Bean が存在するかどうかを自動的に検出し、存在する場合はデフォルトのメトリクスファクトリの代わりにそれを使用します。

これらのレガシーメトリクスは、以下で説明する Micrometer メトリクスを推奨して非推奨となりました。レガシーメトリクスのサポートは、将来のリリースで削除される予定です。

Micrometer 統合

バージョン 5.0.3 以降、アプリケーションコンテキストに Micrometer (英語)  MeterRegistry が存在すると、組み込みメトリクスに加えて Micrometer メトリクスのサポートがトリガーされます(従来の組み込みメトリクスは将来のリリースで削除されることに注意してください)。

Micrometer は、バージョン 5.0.2 で最初にサポートされましたが、バージョン 5.0.3 で Micrometer Meters に変更が加えられ、次元システムでの使用により適したものになりました。5.0.4 ではさらに変更が加えられました。Micrometer を使用する場合、5.0.4 の変更の一部が API の変更を壊していたため、最小限のバージョン 5.0.4 をお勧めします。

Micrometer を使用するには、MeterRegistry Bean の 1 つをアプリケーションコンテキストに追加します。IntegrationManagementConfigurer は、MeterRegistry Bean を 1 つだけ検出した場合、integrationMicrometerMetricsCaptor という名前で MicrometerMetricsCaptor Bean を構成します。

MessageHandler および MessageChannel ごとに、タイマーが登録されます。MessageSource ごとに、カウンターが登録されます。

これは、AbstractMessageHandlerAbstractMessageChannelAbstractMessageSource を継承するオブジェクトにのみ適用されます(ほとんどのフレームワークコンポーネントに当てはまります)。

Micrometer メトリクスでは、統計のキャプチャーは Micrometer に委譲されるため、statsEnabled フラグは効果がありません。countsEnabled フラグは、各メッセージを処理するときに Micrometer Meter インスタンスを更新するかどうかを制御します。

メッセージチャネルの送信操作用の Timer メーターには、次の名前またはタグがあります。

  • namespring.integration.send

  • tagtype:channel

  • tagname:<componentName>

  • tagresult:(success|failure)

  • tagexception:(none|exception simple class name)

  • descriptionSend processing time

none 例外を含む failure 結果は、チャネルの send() 操作が false を返したことを意味します)

ポーリング可能なメッセージチャネルでの受信操作用の Counter メーターには、次の名前またはタグがあります。

  • namespring.integration.receive

  • tagtype:channel

  • tagname:<componentName>

  • tagresult:(success|failure)

  • tagexception:(none|exception simple class name)

  • descriptionMessages received

メッセージハンドラーの操作用の Timer メーターには、次の名前またはタグがあります。

  • namespring.integration.send

  • tagtype:handler

  • tagname:<componentName>

  • tagresult:(success|failure)

  • tagexception:(none|exception simple class name)

  • descriptionSend processing time

メッセージソースの Counter メーターには、次の名前 / タグがあります。

  • namespring.integration.receive

  • tagtype:source

  • tagname:<componentName>

  • tagresult:success

  • tagexception:none

  • descriptionMessages received

さらに、3 つの Gauge メーターがあります。

  • spring.integration.channels: アプリケーション内の MessageChannels の数。

  • spring.integration.handlers: アプリケーション内の MessageHandlers の数。

  • spring.integration.sources: アプリケーション内の MessageSources の数。

MicrometerMetricsCaptor のサブクラスを提供することにより、統合コンポーネントによって作成された Meters の名前とタグをカスタマイズできます。MicrometerCustomMetricsTests [GitHub] (英語) テストケースは、その方法の簡単な例を示しています。ビルダーサブクラスの build() メソッドをオーバーロードすることにより、メーターをさらにカスタマイズすることもできます。

MessageChannel メトリクス機能

これらのレガシーメトリクスは、将来のリリースで削除される予定です。Micrometer 統合を参照してください。

メッセージチャネルは、具象型に従ってメトリクスをレポートします。DirectChannel を見ている場合、送信操作の統計が表示されます。QueueChannel の場合、受信操作の統計と、この QueueChannel によって現在バッファリングされているメッセージの数も表示されます。どちらの場合も、一部のメトリクスは単純なカウンター(メッセージカウントとエラーカウント)であり、一部は興味深い量の平均の推定値です。これらの推定値の計算に使用されるアルゴリズムについて、次の表で簡単に説明します。

表 1: MessageChannel メトリクス
メトリクス型 サンプル アルゴリズム

カウント

送信数

シンプルなインクリメンター。イベントが発生すると 1 ずつ増加します。

エラー数

エラーカウントの送信

シンプルなインクリメンター。送信でエラーが発生すると、1 ずつ増加します。

持続時間

送信時間 (ミリ秒単位のメソッド実行時間)

減衰係数を伴う指数移動平均(デフォルトでは 10)。ほぼ最後の 10 回(デフォルト)の測定でのメソッド実行時間の平均。

レート

送信レート (1 秒あたりの操作数)

時間の減衰があるイベント間の間隔の指数移動平均の逆数(デフォルトでは 60 秒以上経過)および測定ごと(デフォルトでは最後の 10 イベント)。

エラー率

エラー率を送信 (1 秒あたりのエラー数)

時間の減衰を伴うエラーイベント(デフォルトでは 60 秒以上)と測定単位(デフォルトでは最後の 10 イベント)の間隔の指数移動平均の逆数。

比率

成功率を送信 (合計送信に対する成功の比率)

値で構成される系列の指数移動平均として成功率を推定します(成功の場合は 1、失敗の場合は 0、デフォルトでは時間とイベントに伴うレート測定に従って減衰します)。エラー率は次のとおりです。1- 成功率。

MessageHandler メトリクス機能

これらのレガシーメトリクスは、将来のリリースで削除される予定です。Micrometer 統合を参照してください。

次の表は、メッセージハンドラーについて維持される統計を示しています。一部のメトリクスは単純なカウンター(メッセージカウントとエラーカウント)であり、1 つは送信期間の平均の推定値です。これらの推定値の計算に使用されるアルゴリズムについて、次の表で簡単に説明します。

表 2: MessageHandlerMetrics
メトリクス型 サンプル アルゴリズム

カウント

ハンドル数

シンプルなインクリメンター。イベントが発生すると 1 ずつ増加します。

エラー数

ハンドラーのエラー数

シンプルなインクリメンター。呼び出しでエラーが発生すると、1 ずつ増加します。

アクティブカウント

ハンドラーのアクティブカウント

現在ハンドラー(または任意のダウンストリーム同期フロー)を呼び出している現在アクティブなスレッドの数を示します。

持続時間

処理時間 (ミリ秒単位のメソッド実行時間)

減衰係数を伴う指数移動平均(デフォルトでは 10)。およそ最後の 10 回(デフォルト)の測定でのメソッド実行時間の平均。

時間ベースの平均推定

時間ベースの平均推定値の特徴は、新しい測定値が到着しなければ時間とともに減衰することです。経時的な動作の解釈に役立つように、最後の測定からの時間(秒単位)もメトリクスとして公開されます。

2 つの基本的な指数モデルがあります: 測定ごとの減衰(期間と測定回数がメトリクスの一部である場合に適切)および時間単位ごとの減衰(測定間の時間がメトリクスの一部であるレート測定により適しています)。両方のモデルは、w(i) = r^i が r=constantS(n) = x(n) + r S(n-1) の場合、S(n) = sum(i=0,i=n) w(i) x(i) が特別な形式を持っているという事実に依存します(したがって、S(n-1) (シリーズ x(i) 全体ではなく)のみを保存して、最後の測定から新しいメトリクス推定値を生成します)。期間メトリクスで使用されるアルゴリズムは、r=exp(-1/M) と M=10 を使用します。最終的な影響は、推定値 S(n) が最近の測定値により大きく重み付けされ、おおよそ最後の M 測定値で構成されることです。M は推定の「ウィンドウ」または失効率です。バニラ移動平均の場合、i は測定回数のカウンターです。レートについては、i を経過時間または経過時間とカウンターの組み合わせとして解釈します(したがって、メトリクス推定値には、最後の M 測定値と最後の T 秒からのおおよそのコントリビュートが含まれます)。

メトリクスファクトリ

MessageChannel インスタンスおよび MessageHandler インスタンスにカスタムチャネルメトリクスを提供できるように、戦略インターフェース MetricsFactory が導入されました。デフォルトでは、DefaultMetricsFactory は MessageChannelMetrics と MessageHandlerMetrics のデフォルト実装を提供し、先に説明しました。デフォルトの MetricsFactory をオーバーライドするには、MetricsFactory Bean インスタンスへの参照を提供して、前述のように構成します。次のセクションで説明するように、デフォルトの実装をカスタマイズするか、AbstractMessageChannelMetrics または AbstractMessageHandlerMetrics を継承してまったく異なる実装を提供できます。

Micrometer 統合も参照してください。

前述のデフォルトのメトリクスファクトリに加えて、フレームワークは AggregatingMetricsFactory を提供します。このファクトリは、AggregatingMessageChannelMetrics および AggregatingMessageHandlerMetrics インスタンスを作成します。非常に大容量のシナリオでは、統計をキャプチャーするコストが非常に高くなる可能性があります(システムに 2 回呼び出して、各メッセージのデータを保存する時間)。集約メトリクスは、メッセージのサンプル全体のレスポンス時間を集約します。これにより、CPU 時間を大幅に節約できます。

メッセージがバーストで到着した場合、統計が歪む可能性があります。これらのメトリクスは、高一定のメッセージレートで使用することを目的としています。

次の例は、集約メトリクスファクトリを定義する方法を示しています。

<bean id="aggregatingMetricsFactory"
            class="org.springframework.integration.support.management.AggregatingMetricsFactory">
    <constructor-arg value="1000" /> <!-- sample size -->
</bean>

上記の構成では、1000 メッセージを超える期間が集約されます。カウント(送信およびエラー)はメッセージごとに維持されますが、統計はメッセージ 1000 個ごとです。

デフォルトのチャネルおよびハンドラー統計のカスタマイズ

これらの値の詳細については、ExponentialMovingAverage* クラスの時間ベースの平均推定および Javadoc を参照してください。

デフォルトでは、DefaultMessageChannelMetrics および DefaultMessageHandlerMetrics は、10 回の測定の「ウィンドウ」、1 秒のレート期間(1 秒あたりの平均レート)、1 分の減衰経過期間を使用します。

これらのデフォルトをオーバーライドする場合は、適切に構成されたメトリクスを返すカスタム MetricsFactory を提供し、前述のように MBean エクスポーターでそれへの参照を提供できます。

次の例は、その方法を示しています。

public static class CustomMetrics implements MetricsFactory {

    @Override
    public AbstractMessageChannelMetrics createChannelMetrics(String name) {
        return new DefaultMessageChannelMetrics(name,
                new ExponentialMovingAverage(20, 1000000.),
                new ExponentialMovingAverageRate(2000, 120000, 30, true),
                new ExponentialMovingAverageRatio(130000, 40, true),
                new ExponentialMovingAverageRate(3000, 140000, 50, true));
    }

    @Override
    public AbstractMessageHandlerMetrics createHandlerMetrics(String name) {
        return new DefaultMessageHandlerMetrics(name, new ExponentialMovingAverage(20, 1000000.));
    }

}
高度なカスタマイズ

前述のカスタマイズは大規模であり、MBean エクスポーターによってエクスポートされるすべての適切な Bean に適用されます。これは、XML 構成を使用するときに利用できるカスタマイズの範囲です。

AbstractMessageChannel および AbstractMessageHandler で configureMetrics メソッドを呼び出すことにより、Java @Configuration を使用して、または実行時にプログラムで(アプリケーションコンテキストがリフレッシュされた後)個別の Bean に異なる実装を提供できます。

パフォーマンスの改善

以前は、時間ベースのメトリクス(時間ベースの平均推定を参照)はリアルタイムで計算されていました。代わりに、取得時に統計が計算されるようになりました。これにより、統計ごとに少量のメモリが追加されますが、パフォーマンスが大幅に向上しました。前述のように、Lifecycle メソッドの呼び出しを許可する MBean を保持したまま、統計を完全に無効にすることができます。

JMX サポート

Spring Integration は、JMX 通知を受信および公開するためのチャネルアダプターを提供します。

この依存関係をプロジェクトに含める必要があります。

<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-jmx</artifactId>
    <version>5.3.0.RELEASE</version>
</dependency>
compile "org.springframework.integration:spring-integration-jmx:5.3.0.RELEASE"

受信チャネルアダプターは JMX MBean 属性値のポーリングを許可し、発信チャネルアダプターは JMX MBean 操作の呼び出しを許可します。

通知リスニングチャネルアダプター

通知リスニングチャネルアダプターには、このリスナーを登録する通知を発行する MBean の JMX ObjectName が必要です。非常に単純な構成は、次のようになります。

<int-jmx:notification-listening-channel-adapter id="adapter"
    channel="channel"
    object-name="example.domain:name=publisher"/>
notification-listening-channel-adapter は起動時に MBeanServer に登録され、デフォルトの Bean 名は mbeanServer です。これは、Spring の <context:mbean-server/> 要素を使用したときに生成される Bean 名と同じです。別の名前を使用する必要がある場合は、mbean-server 属性を必ず含めてください。

アダプターは、NotificationFilter および「ハンドバック」オブジェクトへの参照を受け入れて、各通知で戻されるコンテキストを提供することもできます。これらの属性は両方ともオプションです。上記の例を拡張して、これらの属性と明示的な MBeanServer Bean 名を含めると、次の例が生成されます。

<int-jmx:notification-listening-channel-adapter id="adapter"
    channel="channel"
    mbean-server="someServer"
    object-name="example.domain:name=somePublisher"
    notification-filter="notificationFilter"
    handback="myHandback"/>

_Notification-listening チャネルアダプターはイベント駆動型であり、MBeanServer に直接登録されます。ポーラー構成は必要ありません。

このコンポーネントの場合のみ、object-name 属性にはオブジェクト名パターンを含めることができます(たとえば、「org.something:type = MyType、name = *」)。その場合、アダプターは、パターンに一致するオブジェクト名を持つすべての MBean から通知を受け取ります。さらに、次の例に示すように、object-name 属性には、オブジェクト名パターンの <util:list> への SpEL 参照を含めることができます。

<jmx:notification-listening-channel-adapter id="manyNotificationsAdapter"
    channel="manyNotificationsChannel"
    object-name="#{patterns}"/>

<util:list id="patterns">
    <value>org.foo:type=Foo,name=*</value>
    <value>org.foo:type=Bar,name=*</value>
</util:list>

DEBUG レベルのロギングが有効になっている場合、検出された MBean の名前が記録されます。

通知発行チャネルアダプター

通知発行チャネルアダプターは比較的単純です。次の例に示すように、構成に必要なのは JMX オブジェクト名のみです。

<context:mbean-export/>

<int-jmx:notification-publishing-channel-adapter id="adapter"
    channel="channel"
    object-name="example.domain:name=publisher"/>

また、MBeanExporter がコンテキストに存在する必要があります。そのため、前の例で <context:mbean-export/> 要素も示されています。

このアダプターのチャネルにメッセージが送信されると、メッセージのコンテンツから通知が作成されます。ペイロードが String である場合、通知の message テキストとして渡されます。その他のペイロード型は、通知の userData として渡されます。

JMX 通知には type もあり、ドット区切りの String である必要があります。type を提供するには 2 つの方法があります。JmxHeaders.NOTIFICATION_TYPE キーに関連付けられたメッセージヘッダー値には常に優先順位が与えられます。または、次の例に示すように、構成でフォールバック default-notification-type 属性を提供できます。

<context:mbean-export/>

<int-jmx:notification-publishing-channel-adapter id="adapter"
    channel="channel"
    object-name="example.domain:name=publisher"
    default-notification-type="some.default.type"/>

属性ポーリングチャネルアダプター

属性ポーリングチャネルアダプターは、MBean を介して管理属性として使用可能な値を定期的に確認する必要がある場合に役立ちます。Spring Integration の他のポーリングアダプターと同じ方法でポーラーを構成できます(またはデフォルトのポーラーを使用できます)。object-name と attribute-name が必要です。MBeanServer の参照も必要です。ただし、デフォルトでは、それが自動的に mbeanServer という名前 Bean ためのチェックは、通知リスニングチャネルアダプターと同じでは先に述べました。次の例は、属性ポーリングチャネルアダプターを XML で構成する方法を示しています。

<int-jmx:attribute-polling-channel-adapter id="adapter"
    channel="channel"
    object-name="example.domain:name=someService"
    attribute-name="InvocationCount">
        <int:poller max-messages-per-poll="1" fixed-rate="5000"/>
</int-jmx:attribute-polling-channel-adapter>

ツリーポーリングチャネルアダプター

ツリーポーリングチャネルアダプターは、JMX MBean ツリーにクエリを実行し、クエリに一致するオブジェクトのグラフであるペイロードを含むメッセージを送信します。デフォルトでは、MBean はプリミティブと、MapList、配列などの単純なオブジェクトにマップされます。そうすることで、(たとえば)JSON への単純な変換が可能になります。MBeanServer 参照も必要です。ただし、デフォルトでは、それが自動的に mbeanServer という名前 Bean ためのチェックは、通知リスニングチャネルアダプターと同じでは先に述べました。次の例は、XML を使用してツリーポーリングチャネルアダプターを設定する方法を示しています。

<int-jmx:tree-polling-channel-adapter id="adapter"
    channel="channel"
    query-name="example.domain:type=*">
        <int:poller max-messages-per-poll="1" fixed-rate="5000"/>
</int-jmx:tree-polling-channel-adapter>

上記の例には、選択した MBean のすべての属性が含まれています。適切なフィルターが構成された MBeanObjectConverter を提供することにより、属性をフィルターに掛けることができます。converter 属性を使用して Bean 定義への参照としてコンバーターを提供するか、内部 <bean/> 定義を使用できます。Spring Integration は、コンストラクター引数で MBeanAttributeFilter を取ることができる DefaultMBeanObjectConverter を提供します。

Spring Integration は 2 つの標準フィルターを提供します。NamedFieldsMBeanAttributeFilter では、含める属性のリストを指定できます。NotNamedFieldsMBeanAttributeFilter では、除外する属性のリストを指定できます。独自のフィルターを実装することもできます。

操作呼び出しチャネルアダプター

操作呼び出しチャネルアダプターを使用すると、MBean によって公開されるすべての管理操作のメッセージ駆動型呼び出しが可能になります。各呼び出しには、呼び出される操作名とターゲット MBean のオブジェクト名が必要です。これらは両方とも、アダプター構成または JmxHeaders.OBJECT_NAME および JmxHeaders.OPERATION_NAME メッセージヘッダーを介してそれぞれ明示的に提供する必要があります。

<int-jmx:operation-invoking-channel-adapter id="adapter"
    object-name="example.domain:name=TestBean"
    operation-name="ping"/>

その場合、アダプターは mbeanServer Bean を検出できる必要があるだけです。別の Bean 名が必要な場合は、mbean-server 属性に参照を提供します。

メッセージのペイロードは、操作のパラメーター(存在する場合)にマップされます。String キーを持つ Map -typed ペイロードは名前 / 値のペアとして扱われますが、List または配列は単純な引数リストとして渡されます(明示的なパラメーター名はありません)。操作に単一のパラメーター値が必要な場合、ペイロードはその単一の値を表すことができます。また、操作にパラメーターが必要ない場合、ペイロードは無視されます。

ヘッダーを含む必要のないメッセージによって呼び出される単一の共通操作のチャネルを公開する場合、その最後のオプションはうまく機能します。

操作呼び出し送信ゲートウェイ

操作を呼び出すチャネルアダプターと同様に、Spring Integration は操作を呼び出す送信ゲートウェイも提供します。これは、戻り値が必要な場合に非 void 操作を処理するときに使用できます。戻り値は、メッセージペイロードとしてゲートウェイによって指定された reply-channel に送信されます。次の例は、XML を使用して操作呼び出し発信ゲートウェイを構成する方法を示しています。

<int-jmx:operation-invoking-outbound-gateway request-channel="requestChannel"
   reply-channel="replyChannel"
   object-name="o.s.i.jmx.config:type=TestBean,name=testBeanGateway"
   operation-name="testWithReturn"/>

reply-channel 属性を指定しない場合、応答メッセージは IntegrationMessageHeaderAccessor.REPLY_CHANNEL ヘッダーで識別されるチャネルに送信されます。通常、このヘッダーは、ゲートウェイコンポーネントなどのメッセージフローへのエントリポイントによって自動作成されます。ただし、Spring Integration メッセージを手動で作成してチャネルに直接送信することによってメッセージフローが開始された場合、メッセージヘッダーを明示的に指定するか、reply-channel 属性を使用する必要があります。

MBean エクスポーター

Spring Integration コンポーネントは、IntegrationMBeanExporter の構成時に MBean として公開される場合があります。IntegrationMBeanExporter のインスタンスを作成するには、Bean を定義し、MBeanServer への参照とドメイン名(必要な場合)を提供します。ドメインを除外できます。この場合、デフォルトのドメインは org.springframework.integration です。次の例は、IntegrationMBeanExporter および関連する MBeanServer インスタンスのインスタンスを宣言する方法を示しています。

<int-jmx:mbean-export id="integrationMBeanExporter"
            default-domain="my.company.domain" server="mbeanServer"/>

<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
    <property name="locateExistingServerIfPossible" value="true"/>
</bean>

MBean エクスポーターは、Spring コアで提供されるものと直交しています。メッセージチャネルとメッセージハンドラーを登録しますが、それ自体は登録しません。標準の <context:mbean-export/> タグを使用して、エクスポーター自体(および Spring Integration の他の特定のコンポーネント)を公開できます。エクスポーターには、アクティブなハンドラーの数やキューに入っているメッセージの数など、いくつかのメトリクスが添付されています。

正常なシャットダウン管理操作で説明したように、便利な操作もあります。

Spring Integration 4.0 は、@Configuration クラスレベルでいくつかの便利なオプションを備えた型 IntegrationMBeanExporter のデフォルト integrationMbeanExporter Bean の便利な設定を可能にする @EnableIntegrationMBeanExport アノテーションを導入しました。次の例は、この Bean を構成する方法を示しています。

@Configuration
@EnableIntegration
@EnableIntegrationMBeanExport(server = "mbeanServer", managedComponents = "input")
public class ContextConfiguration {

	@Bean
	public MBeanServerFactoryBean mbeanServer() {
		return new MBeanServerFactoryBean();
	}
}

より多くのオプションを提供したり、複数の IntegrationMBeanExporter Bean が必要な場合(異なる MBean サーバー用、または @EnableMBeanExport などを介して標準 Spring MBeanExporter との競合を避けるため)、IntegrationMBeanExporter を汎用 Bean として構成できます。

MBean オブジェクト名

アプリケーション内のすべての MessageChannelMessageHandlerMessageSource インスタンスは、管理および監視機能を提供するために MBean エクスポーターによってラップされます。各コンポーネント型に対して生成された JMX オブジェクト名を次の表に示します。

表 3: MBean オブジェクト名
コンポーネントタイプ オブジェクト名

MessageChannel

 `o.s.i:type=MessageChannel,name=<channelName>`

MessageSource

 `o.s.i:type=MessageSource,name=<channelName>,bean=<source>`

MessageHandler

 `o.s.i:type=MessageSource,name=<channelName>,bean=<source>`

ソースおよびハンドラーのオブジェクト名の bean 属性は、次の表のいずれかの値を取ります。

表 4: Bean ObjectName パーツ
Bean 値 説明

endpoint

囲んでいるエンドポイントの Bean 名(例: <service-activator>)(ある場合)

anonymous

囲んでいるエンドポイントにユーザー指定の Bean 名がなかったことを示すため、JMX 名は入力チャネル名です。

internal

よく知られている Spring Integration のデフォルトコンポーネント

ハンドラー / ソース

上記のどれでもない。監視されているオブジェクトの toString() メソッドにフォールバックします (ハンドラーまたはソース)

object-name-static-properties 属性で Properties オブジェクトへの参照を提供することにより、オブジェクト名にカスタム要素を追加できます。

また、Spring Integration 3.0 以降、object-naming-strategy 属性を設定することにより、カスタム ObjectNamingStrategy (Javadoc) を使用できます。そうすることで、すべての統合 MBean を「統合」型にグループ化するなど、MBean の命名をより詳細に制御できます。次の例は、1 つの可能なカスタム命名戦略の実装を示しています。

public class Namer implements ObjectNamingStrategy {

	private final ObjectNamingStrategy realNamer = new KeyNamingStrategy();
	@Override
	public ObjectName getObjectName(Object managedBean, String beanKey) throws MalformedObjectNameException {
		String actualBeanKey = beanKey.replace("type=", "type=Integration,componentType=");
		return realNamer.getObjectName(managedBean, actualBeanKey);
	}

}

beanKey 引数は String であり、default-domain で始まり、追加の静的プロパティを含む標準オブジェクト名が含まれています。前の例では、標準の type パーツを componentType に移動し、type を「統合」に設定して、1 つのクエリですべての統合 MBean を選択できるようにします: `my.domain:type=Integration,*`。そうすることで、VisualVM などのツールのドメインにある 1 つのツリーエントリに Bean をグループ化することもできます。

デフォルトの命名戦略は MetadataNamingStrategy (Javadoc) です。エクスポーターは、default-domain をそのオブジェクトに伝搬して、Bean キーの解析が失敗した場合にフォールバックオブジェクト名を生成できるようにします。カスタム命名戦略が MetadataNamingStrategy (またはそのサブクラス)の場合、エクスポーターは default-domain を伝搬しません。戦略 Bean で設定する必要があります。

バージョン 5.1 以降。Java 識別子(またはピリオド .)で許可されていない文字が含まれている場合、Bean 名(オブジェクト名の name キーで表される)は引用されます。

JMX の改善

バージョン 4.2 はいくつかの重要な改善を導入し、フレームワークでの JMX サポートのかなり大きなオーバーホールを表します。これらにより、JMX 統計収集のパフォーマンスが大幅に向上し、その制御が大幅に向上しました。ただし、いくつかの特定の(珍しい)状況では、ユーザーコードにいくつかの影響があります。これらの変更の詳細を以下に示しますが、必要な場合は注意してください。

メトリクスキャプチャー

以前は、MessageSourceMessageChannelMessageHandler メトリクスは、オブジェクトを JDK 動的プロキシでラップして、適切なメソッド呼び出しをインターセプトし、統計をキャプチャーすることによってキャプチャーされていました。プロキシは、統合 MBean エクスポータがコンテキストで宣言されたときに追加されました。

現在、統計は Bean 自体によってキャプチャーされます。詳細については、指標と管理を参照してください。

この変更は、カスタムハンドラーが AbstractMessageHandler を継承しない限り、カスタム MessageHandler 実装の MBean または統計を自動的に取得しないことを意味します。これを解決する最も簡単な方法は、AbstractMessageHandler を継承することです。それができない場合、別の回避策は、MessageHandlerMetrics インターフェースを実装することです。便宜上、DefaultMessageHandlerMetrics が提供され、統計をキャプチャーおよびレポートします。beforeHandle および afterHandle を適切なタイミングで呼び出す必要があります。MessageHandlerMetrics メソッドは、このオブジェクトに委譲して、各統計を取得できます。同様に、MessageSource 実装は AbstractMessageSource を継承するか、MessageSourceMetrics を実装する必要があります。メッセージソースはカウントのみをキャプチャーするため、便利なクラスは提供されていません。AtomicLong フィールドでカウントを維持する必要があります。

プロキシを削除すると、さらに 2 つの利点があります。

  • プロキシがスタック上にないため、例外のスタックトレースが減少します(JMX が有効な場合)。

  • 同じ Bean に対して 2 つの MBean がエクスポートされた場合、属性と操作が統合された単一の MBean のみがエクスポートされるようになりました(MBean 統合箇条書きを参照)。

レゾリューション

System.currentTimeMillis() ではなく)System.nanoTime() が、時間をキャプチャーするために使用されるようになりました。これにより、特に 1 ミリ秒未満の継続時間が予想される場合に、一部の JVM でより正確になります。

初期統計収集状態の設定

以前は、JMX を有効にすると、すべてのソース、チャネル、ハンドラーが統計をキャプチャーしました。個々のコンポーネントで統計を有効にするかどうかを制御できるようになりました。さらに、完全な時間ベースの統計をキャプチャーする代わりに、MessageChannel インスタンスと MessageHandler インスタンスで単純なカウントをキャプチャーできます。これは、詳細な統計が必要な場所を選択的に構成し、実行時に収集を有効または無効にすることができるため、パフォーマンスに大きな影響を与える可能性があります。

指標と管理を参照してください。

@IntegrationManagedResource

@ManagedResource アノテーションと同様に、@IntegrationManagedResource はクラスを MBean としてエクスポートする資格があるものとしてマークします。ただし、アプリケーションコンテキストに IntegrationMBeanExporter がある場合にのみエクスポートされます。

以前に `@ManagedResource` でアノテーションが付けられていた特定の Spring Integration クラス(org.springframework.integration 内)パッケージには、@ManagedResource と @IntegrationManagedResource の両方でアノテーションが付けられるようになりました。これは下位互換性のためです(次の項目を参照)。このような MBean は、任意のコンテキスト MBeanServer または IntegrationMBeanExporter によってエクスポートされます(両方ではありません。両方のエクスポーターが存在する場合、Bean が managed-components パターンと一致すると、Bean は統合エクスポーターによってエクスポートされます)。

統合された MBean

フレームワーク内の特定のクラス(マッピングルーターなど)には、メトリクスと Lifecycle によって提供される属性と操作に加えて、追加の属性と操作があります。ここでは、例として Router を使用します。

以前は、これらの型の Bean は 2 つの別個の MBean としてエクスポートされていました。

  • メトリクス MBean(intDomain:type=MessageHandler,name=myRouter,bean=endpoint などのオブジェクト名を持つ)。この MBean には、メトリクス属性とメトリクス / ライフサイクル操作がありました。

  • 2 番目の MBean(ctxDomain:name=org.springframework.integration.config. RouterFactoryBean#0、type = MethodInvokingRouter` などのオブジェクト名)が、チャネルマッピング属性と操作とともにエクスポートされました。

    これで、属性と操作が単一の MBean に統合されました。オブジェクト名はエクスポータによって異なります。統合 MBean エクスポーターによってエクスポートされる場合、オブジェクト名は、例: intDomain:type=MessageHandler,name=myRouter,bean=endpoint です。別のエクスポーターによってエクスポートされた場合、オブジェクト名は次のようになります。例: ctxDomain:name=org.springframework.integration.config. RouterFactoryBean#0,type=MethodInvokingRouter。統合エクスポータ以外のエクスポータによって統計が有効にされていない(属性は 0 である)ことを除いて、これらの MBean(オブジェクト名を除く)の間に違いはありません。JMX 操作を使用して、実行時に統計を有効にできます。統合 MBean エクスポーターによってエクスポートされる場合、初期状態は前述のように管理できます。

    現在 2 番目の MBean を使用して、たとえばチャネルマッピングを変更し、統合 MBean エクスポーターを使用している場合、MBean の統合によりオブジェクト名が変更されていることに注意してください。統合 MBean エクスポーターを使用していない場合、変更はありません。
MBean エクスポーター Bean の名前パターン

以前は、managed-components パターンは包括的のみでした。Bean 名がパターンの 1 つと一致した場合、その名前が含まれます。これで、パターンの前に ! を付けることで、パターンを無効にすることができます。例: !thing*, things は、things を除き、thing で始まらないすべての Bean 名に一致します。パターンは左から右に評価されます。最初の一致(正または負)が勝ち、それ以降のパターンは適用されません。

この構文をパターンに追加すると、1 つの可能性のある(おそらくありそうもない)問題が発生します。"!thing" という名前の Bean があり、MBean エクスポーターの managed-components パターンに !thing のパターンを含めた場合、一致しなくなります。パターンは thing という名前ではないすべての Bean に一致するようになりました。この場合、\ を使用して、パターン内の ! をエスケープできます。\!thing パターンは、!thing という名前の Bean と一致します。
IntegrationMBeanExporter の変更

IntegrationMBeanExporter は SmartLifecycle を実装しなくなりました。これは、MBean の登録および登録解除に start() および stop() 操作が使用できなくなったことを意味します。MBean は、コンテキストの初期化中に登録され、コンテキストが破棄されると登録解除されます。

正常なシャットダウン管理操作

MBean エクスポーターは、JVM を終了する前に使用することを目的とした、秩序正しい方法でアプリケーションをシャットダウンする JMX 操作を提供します。次の例は、その使用方法を示しています。

public void stopActiveComponents(long howLong)

その使用と操作は正常なシャットダウンで説明されています。

メッセージ履歴

メッセージングアーキテクチャの主な利点は、参加しているコンポーネントが相互の認識を維持できないように、疎結合です。この事実だけで、アプリケーションは非常に柔軟になり、残りのフローに影響を与えずにコンポーネントを変更したり、メッセージングルートを変更したり、メッセージ消費スタイルを変更したりできます(ポーリングとイベントドリブン)。しかし、この控えめなスタイルのアーキテクチャは、物事がうまくいかない場合には難しいことがわかります。デバッグするときは、おそらくメッセージに関する情報(その発信元、通過したチャネル、その他の詳細)をできるだけ多く取得する必要があります。

メッセージ履歴は、デバッグまたは監査証跡を維持するために、メッセージパスの認識レベルを維持するオプションを提供することで役立つパターンの 1 つです。Spring 統合は、メッセージにヘッダーを追加し、メッセージが追跡対象コンポーネントを通過するたびにそのヘッダーを更新することにより、メッセージフローを設定してメッセージ履歴を維持する簡単な方法を提供します。

メッセージ履歴の構成

メッセージ履歴を有効にするには、次の例に示すように、構成で message-history 要素のみを定義する必要があります。

<int:message-history/>

これで、すべての名前付きコンポーネント( "id" が定義されているコンポーネント)が追跡されます。フレームワークは、メッセージに "history" ヘッダーを設定します。その値は List<Properties> です。

次の構成例を検討してください。

<int:gateway id="sampleGateway" 
    service-interface="org.springframework.integration.history.sample.SampleGateway"
    default-request-channel="bridgeInChannel"/>

<int:chain id="sampleChain" input-channel="chainChannel" output-channel="filterChannel">
  <int:header-enricher>
    <int:header name="baz" value="baz"/>
  </int:header-enricher>
</int:chain>

上記の構成により、単純なメッセージ履歴構造が生成され、出力は次のようになります。

[{name=sampleGateway, type=gateway, timestamp=1283281668091},
 {name=sampleChain, type=chain, timestamp=1283281668094}]

メッセージ履歴にアクセスするには、MessageHistory ヘッダーにアクセスするだけで済みます。次の例は、その方法を示しています。

Iterator<Properties> historyIterator =
    message.getHeaders().get(MessageHistory.HEADER_NAME, MessageHistory.class).iterator();
assertTrue(historyIterator.hasNext());
Properties gatewayHistory = historyIterator.next();
assertEquals("sampleGateway", gatewayHistory.get("name"));
assertTrue(historyIterator.hasNext());
Properties chainHistory = historyIterator.next();
assertEquals("sampleChain", chainHistory.get("name"));

すべてのコンポーネントを追跡したくない場合があります。名前に基づいて特定のコンポーネントに履歴を制限するには、tracked-components 属性を指定し、追跡するコンポーネントに一致するコンポーネント名とパターンのコンマ区切りリストを指定できます。次の例は、その方法を示しています。

<int:message-history tracked-components="*Gateway, sample*, aName"/>

前の例では、'Gateway' で終わる、'sample' で始まる、または名前 'aName' と完全に一致するコンポーネントに対してのみ、メッセージ履歴が保持されます。

バージョン 4.0 から、@Configuration クラスで @EnableMessageHistory アノテーションを使用することもできます。さらに、MessageHistoryConfigurer Bean は IntegrationMBeanExporter (MBean エクスポーターを参照)によって JMX MBean として公開され、実行時にパターンを変更できるようになりました。ただし、パターンを変更するには、Bean を停止する(メッセージ履歴をオフにする)必要があることに注意してください。この機能は、一時的に履歴をオンにしてシステムを分析するのに役立つ場合があります。MBean のオブジェクト名は <domain>:name=messageHistoryConfigurer,type=MessageHistoryConfigurer です。

複数の Bean(@EnableMessageHistory および <message-history/> によって宣言されている)が存在する場合、すべて同じコンポーネント名パターンを持っている必要があります(トリミングおよびソート時)。MessageHistoryConfigurer に汎用 <bean/> 定義を使用しないでください。
定義により、メッセージ履歴ヘッダーは不変です(履歴を書き換えることはできません)。メッセージ履歴値を書き込むとき、コンポーネントは新しいメッセージを作成するか(コンポーネントがオリジンの場合)、リクエストメッセージから履歴をコピーし、それを変更して応答メッセージに新しいリストを設定します。どちらの場合でも、メッセージ自体がスレッドの境界を越えている場合でも、値を追加できます。つまり、履歴値により、非同期メッセージフローのデバッグが大幅に簡素化されます。

メッセージストア

エンタープライズ統合パターン (英語) (EIP)ブックは、メッセージをバッファリングする機能を持ついくつかのパターンを識別します。例: アグリゲーターは、解放できるまでメッセージをバッファリングし、QueueChannel は、コンシューマーがそのチャネルからメッセージを明示的に受信するまでメッセージをバッファリングします。メッセージフロー内の任意の時点で発生する可能性がある障害のため、メッセージをバッファリングする EIP コンポーネントは、メッセージが失われる可能性のあるポイントも導入します。

メッセージを失うリスクを軽減するために、EIP はメッセージストア (英語) パターンを定義します。これにより、EIP コンポーネントは通常、ある種の永続ストア(RDBMS など)にメッセージを保存 (英語) できます。

Spring Integration は、以下によってメッセージストアパターンのサポートを提供します。

  • org.springframework.integration.store.MessageStore 戦略インターフェースの定義

  • このインターフェースのいくつかの実装を提供する

  • MessageStore インターフェースを実装するインスタンスを挿入できるように、メッセージをバッファリングする機能を持つすべてのコンポーネントで message-store 属性を公開します。

特定のメッセージストア実装の設定方法と特定のバッファリングコンポーネントへの MessageStore 実装の注入方法の詳細は、マニュアル全体で説明されています(QueueChannelアグリゲーター遅延器などの特定のコンポーネントを参照)。次の例のペアは、QueueChannel およびアグリゲーターのメッセージストアへの参照を追加する方法を示しています。

例 1: QueueChannel
<int:channel id="myQueueChannel">
    <int:queue message-store="refToMessageStore"/>
<int:channel>
例 2: アグリゲーター
<int:aggregator … message-store="refToMessageStore"/>

デフォルトでは、メッセージは MessageStore の実装である o.s.i.store.SimpleMessageStore を使用してメモリに保存されます。これは、非永続メッセージの潜在的な損失が懸念されない開発環境または単純な低ボリューム環境では問題ない場合があります。ただし、一般的な本番アプリケーションには、メッセージ損失のリスクを軽減するだけでなく、潜在的なメモリ不足エラーを回避するために、より堅牢なオプションが必要です。そのため、さまざまなデータストア用の MessageStore 実装も提供しています。以下は、サポートされている実装の完全なリストです。

ただし、MessageStore の永続的な実装を使用するときは、いくつかの制限に注意してください。

メッセージデータ(ペイロードとヘッダー)は、MessageStore の実装に応じて、異なる直列化戦略を使用して直列化および非直列化されます。例: JdbcMessageStore を使用する場合、Serializable データのみがデフォルトで保持されます。この場合、直列化が発生する前に、直列化できないヘッダーが削除されます。また、トランスポートアダプター(FTP、HTTP、JMS など)によって挿入されるプロトコル固有のヘッダーにも注意してください。例: <http:inbound-channel-adapter/> は HTTP ヘッダーをメッセージヘッダーにマッピングします。そのうちの 1 つは、直列化できない org.springframework.http.MediaType インスタンスの ArrayList です。ただし、Serializer および Deserializer 戦略インターフェースの独自の実装をいくつかの MessageStore 実装(JdbcMessageStore など)に注入して、シリアライゼーションおよびデシリアライゼーションの動作を変更できます。

特定の型のデータを表すヘッダーに特に注意してください。例: ヘッダーの 1 つに Spring Bean のインスタンスが含まれている場合、逆直列化すると、その Bean の別のインスタンスになり、フレームワークによって作成された暗黙的なヘッダーの一部(REPLY_CHANNEL や ERROR_CHANNEL など)に直接影響する場合があります。現在、それらは直列化できませんが、仮にそうであっても、逆直列化されたチャネルは期待されるインスタンスを表しません。

Spring Integration バージョン 3.0 から、HeaderChannelRegistry でチャネルを登録した後にこれらのヘッダーを名前に置き換えるように構成されたヘッダーエンリッチャーでこの課題を解決できます。

また、次のようにメッセージフローを構成するときに何が起こるかを考慮してください: ゲートウェイ→キューチャネル(永続的なメッセージストアによってバッキング)→サービスアクティベーター。そのゲートウェイは一時的な応答チャネルを作成しますが、サービスアクティベーターのポーラーがキューから読み取るまでに失われます。この場合も、ヘッダーエンリッチャーを使用して、ヘッダーを String 表現に置き換えることができます。

詳しくは、ヘッダーエンリッチャーを参照してください。

Spring Integration 4.0 は 2 つの新しいインターフェースを導入しました:

  • ChannelMessageStoreQueueChannel インスタンスに固有の操作を実装するには

  • PriorityCapableChannelMessageStorePriorityChannel インスタンスに使用される MessageStore 実装をマークし、持続メッセージの優先順位を提供するため。

実際の動作は実装によって異なります。このフレームワークは、QueueChannel および PriorityChannel の永続的な MessageStore として使用できる次の実装を提供します。

SimpleMessageStore に関する注意

バージョン 4.1 以降、SimpleMessageStore は getMessageGroup() を呼び出したときにメッセージグループをコピーしなくなりました。大きなメッセージグループの場合、これは重大なパフォーマンスの問題でした。4.0.1 は、この動作を制御できるブール copyOnGet プロパティを導入しました。アグリゲーターによって内部的に使用される場合、このプロパティはパフォーマンスを改善するために false に設定されました。現在は、デフォルトで false です。

アグリゲーターなどのコンポーネントの外部のグループストアにアクセスするユーザーは、コピーの代わりにアグリゲーターが使用しているグループへの直接参照を取得するようになりました。アグリゲーターの外部でグループを操作すると、予測できない結果が生じる可能性があります。

このため、このような操作を実行しないか、copyOnGet プロパティを true に設定する必要があります。

MessageGroupFactory を使用する

バージョン 4.3 から、MessageGroupStore 実装の一部にカスタム MessageGroupFactory 戦略を注入して、MessageGroupStore が使用する MessageGroup インスタンスを作成およびカスタマイズできます。これはデフォルトで SimpleMessageGroupFactory になり、GroupType.HASH_SET (LinkedHashSet)内部コレクションに基づいて SimpleMessageGroup インスタンスを生成します。他の可能なオプションは SYNCHRONISED_SET および BLOCKING_QUEUE です。最後のオプションを使用して、以前の SimpleMessageGroup の動作を復元できます。PERSISTENT オプションも利用できます。詳細については、次のセクションを参照してください。バージョン 5.0.1 から、LIST オプションは、グループ内のメッセージの順序と一意性が重要でない場合にも使用できます。

永続的な MessageGroupStore および遅延ロード

バージョン 4.3 以降、すべての永続的な MessageGroupStore インスタンスは、遅延ロード方式でストアから MessageGroup インスタンスとその messages を取得します。ほとんどの場合、各相関操作でストアから MessageGroup 全体をロードするオーバーヘッドを追加する場合、相関 MessageHandler インスタンス(アグリゲーターおよびリシーケンサーを参照)に役立ちます。

AbstractMessageGroupStore.setLazyLoadMessageGroups(false) オプションを使用して、構成から遅延ロード動作をオフに切り替えることができます。

MongoDB MessageStore (MongoDB メッセージストア)および <aggregator> (アグリゲーター)での遅延ロードのパフォーマンステストでは、次のようなカスタム release-strategy を使用します。

<int:aggregator input-channel="inputChannel"
                output-channel="outputChannel"
                message-store="mongoStore"
                release-strategy-expression="size() == 1000"/>

1000 個の単純なメッセージに対して、次のような結果が生成されます。

...
StopWatch 'Lazy-Load Performance': running time (millis) = 38918
-----------------------------------------
ms     %     Task name
-----------------------------------------
02652  007%  Lazy-Load
36266  093%  Eager
...

メタデータストア

多くの外部システム、サービス、リソースはトランザクション対応ではなく(Twitter、RSS、ファイルシステムなど)、データを既読としてマークする機能はありません。また、場合によっては、一部の統合ソリューションにエンタープライズ統合パターンべき等レシーバー (英語) を実装する必要があります。このゴールを達成し、外部システムとの次の対話の前にエンドポイントの以前の状態を保存するため、または次のメッセージを処理するために、Spring Integration は、一般的なキーと値の契約を持つ org.springframework.integration.metadata.MetadataStore インターフェースの実装としてメタデータストアコンポーネントを提供します。

メタデータストアは、さまざまな種類の汎用メタデータ(たとえば、処理された最後のフィードエントリの公開日)を格納して、フィードアダプターなどのコンポーネントが重複を処理できるように設計されています。コンポーネントに MetadataStore への参照が直接提供されない場合、メタデータストアを見つけるためのアルゴリズムは次のとおりです。最初に、アプリケーションコンテキストで metadataStore ID を持つ Bean を探します。見つかった場合は、それを使用します。そうでない場合は、SimpleMetadataStore の新しいインスタンスを作成します。これは、現在実行中のアプリケーションコンテキストのライフサイクル内でメタデータのみを永続化するメモリ内実装です。つまり、再起動すると、エントリが重複する可能性があります。

アプリケーションコンテキストの再起動間でメタデータを保持する必要がある場合、フレームワークは次の永続的な MetadataStores を提供します。

PropertiesPersistingMetadataStore は、プロパティファイルと PropertiesPersister (Javadoc) によってサポートされています。

デフォルトでは、アプリケーションコンテキストが正常に閉じられたときの状態のみを保持します。Flushable を実装しているため、flush() を呼び出すことで、状態を自由に維持できます。次の例は、XML で "PropertiesPersistingMetadataStore" を構成する方法を示しています。

<bean id="metadataStore"
    class="org.springframework.integration.metadata.PropertiesPersistingMetadataStore"/>

または、MetadataStore インターフェースの独自の実装(たとえば JdbcMetadataStore)を提供し、それをアプリケーションコンテキストで Bean として構成できます。

バージョン 4.0 以降、SimpleMetadataStorePropertiesPersistingMetadataStoreRedisMetadataStore は ConcurrentMetadataStore を実装します。これらはアトミックアップデートを提供し、複数のコンポーネントまたはアプリケーションインスタンスで使用できます。

べき等レシーバーとメタデータストア

メタデータストアは、受信メッセージがすでに処理されており、破棄するか、破棄時に他のロジックを実行できる場合、受信メッセージをフィルターする必要がある場合に、EIP べき等レシーバー (英語) パターンを実装できます。次の構成は、その方法の例を示しています。

<int:filter input-channel="serviceChannel"
			output-channel="idempotentServiceChannel"
			discard-channel="discardChannel"
			expression="@metadataStore.get(headers.businessKey) == null"/>

<int:publish-subscribe-channel id="idempotentServiceChannel"/>

<int:outbound-channel-adapter channel="idempotentServiceChannel"
                              expression="@metadataStore.put(headers.businessKey, '')"/>

<int:service-activator input-channel="idempotentServiceChannel" ref="service"/>

べき等エントリの value は有効期限になる可能性があり、その後、そのエントリは何らかのスケジュールされたリーパーによってメタデータストアから削除される必要があります。

MetadataStoreListener

次の例に示すように、一部のメタデータストア(現在は zookeeper のみ)は、アイテムの変更時にイベントを受信するリスナーの登録をサポートしています。

public interface MetadataStoreListener {

	void onAdd(String key, String value);

	void onRemove(String key, String oldValue);

	void onUpdate(String key, String newValue);
}

詳細については、Javadoc を参照してください。イベントのサブセットのみに関心がある場合は、MetadataStoreListenerAdapter をサブクラス化できます。

制御バス

エンタープライズ統合パターン (英語) (EIP)ブックに従って、コントロールバスの背景となる考え方は、「アプリケーションレベル」メッセージングに使用されるのと同じメッセージングシステムをフレームワーク内のコンポーネントの監視と管理に使用できるということです。Spring Integration では、公開された操作を呼び出す手段としてメッセージを送信できるように、上記のアダプターに基づいて構築しています。

次の例は、XML で制御バスを構成する方法を示しています。

<int:control-bus input-channel="operationChannel"/>

制御バスには、アプリケーションコンテキストで Bean の操作を呼び出すためにアクセスできる入力チャネルがあります。また、エンドポイントをアクティブにするサービスのすべての共通プロパティもあります。例: 操作の結果に、ダウンストリームチャネルに送信する戻り値がある場合、出力チャネルを指定できます。

制御バスは、Spring Expression Language(SpEL)式として入力チャネルでメッセージを実行します。メッセージを受け取り、本文を式にコンパイルし、コンテキストを追加してから実行します。デフォルトのコンテキストは、@ManagedAttribute または @ManagedOperation でアノテーションが付けられたメソッドをサポートします。また、Spring の Lifecycle インターフェース(およびバージョン 5.2 以降の Pausable 拡張)のメソッドをサポートし、Spring の TaskExecutor および TaskScheduler 実装のいくつかを構成するために使用されるメソッドをサポートします。制御バスで独自のメソッドを使用できるようにする最も簡単な方法は、@ManagedAttribute または @ManagedOperation アノテーションを使用することです。これらのアノテーションは、メソッドを JMX MBean レジストリに公開するためにも使用されるため、便利な副産物を提供します。多くの場合、コントロールバスに公開するのと同じ型の操作は、JMX を介して公開するのに適しています。アプリケーションコンテキスト内の特定のインスタンスの解決は、一般的な SpEL 構文で実現されます。これを行うには、Bean の SpEL 接頭辞(@)を Bean 名に指定します。例: Spring Bean でメソッドを実行するために、クライアントは次のように操作チャネルにメッセージを送信できます。

Message operation = MessageBuilder.withPayload("@myServiceBean.shutdown()").build();
operationChannel.send(operation)

式のコンテキストのルートは Message 自体であるため、式内の変数として payload および headers にもアクセスできます。これは、Spring Integration エンドポイントの他のすべての式サポートと一致しています。

Java アノテーションを使用すると、コントロールバスを次のように構成できます。

@Bean
@ServiceActivator(inputChannel = "operationChannel")
public ExpressionControlBusFactoryBean controlBus() {
    return new ExpressionControlBusFactoryBean();
}

同様に、Java DSL フロー定義を次のように構成できます。

@Bean
public IntegrationFlow controlBusFlow() {
    return IntegrationFlows.from("controlBus")
              .controlBus()
              .get();
}

DirectChannel の自動作成でラムダを使用する場合は、次のように制御バスを作成できます。

@Bean
public IntegrationFlow controlBus() {
    return IntegrationFlowDefinition::controlBus;
}

この場合、チャネルの名前は controlBus.input です。

正常なシャットダウン

"MBean エクスポーター" に従って、MBean エクスポーターは stopActiveComponents と呼ばれる JMX 操作を提供します。これは、アプリケーションを正常に停止するために使用されます。操作には単一の Long パラメーターがあります。このパラメーターは、処理中のメッセージの完了を待機する時間(ミリ秒単位)を示します。操作は次のように機能します。

  1. OrderlyShutdownCapable を実装するすべての Bean で beforeShutdown() を呼び出します。

    そうすることで、そのようなコンポーネントはシャットダウンの準備ができます。このインターフェースを実装するコンポーネントの例と、この呼び出しで行うことには、リスナーコンテナーを停止する JMS および AMQP メッセージ駆動型アダプター、新しい接続の受け入れを停止する(既存の接続を開いたまま)TCP サーバー接続ファクトリ、ドロップする TCP 受信エンドポイントが含まれます(ログ)受信した新しいメッセージ、新しいリクエストに対して 503 - Service Unavailable を返す HTTP 受信エンドポイント。

  2. JMS または AMQP-backed チャネルなどのアクティブなチャネルを停止します。

  3. すべての MessageSource インスタンスを停止します。

  4. すべての受信 MessageProducer を停止します(OrderlyShutdownCapable ではありません)。

  5. 操作に渡される Long パラメーターの値で定義されているように、残り時間が残っているのを待ちます。

    そうすることで、飛行中のメッセージがすべての旅を完了することができます。この操作を呼び出すときに適切なタイムアウトを選択することが重要です。

  6. すべての OrderlyShutdownCapable コンポーネントで afterShutdown() を呼び出します。

    これにより、そのようなコンポーネントは最終的なシャットダウンタスクを実行できます(たとえば、開いているすべてのソケットを閉じる)。

正常なシャットダウン管理操作で説明したように、この操作は JMX を使用して呼び出すことができます。プログラムでメソッドを呼び出す場合は、IntegrationMBeanExporter への参照を挿入するか、取得する必要があります。id 属性が <int-jmx:mbean-export/> 定義で指定されていない場合、Bean には名前が生成されます。この名前には、同じ JVM(MBeanServer)に複数の Spring Integration コンテキストが存在する場合に ObjectName の衝突を回避するためのランダムなコンポーネントが含まれています。

このため、プログラムでメソッドを呼び出す場合は、アプリケーションコンテキストで簡単にアクセスできるように、エクスポーターに id 属性を提供することをお勧めします。

最後に、<control-bus> 要素を使用して操作を呼び出すことができます。詳細については、モニタリング Spring Integration サンプルアプリケーション [GitHub] (英語) を参照してください。

前述のアルゴリズムは、バージョン 4.1 で改善されました。以前は、すべてのタスクエグゼキューターとスケジューラーが停止していました。これにより、QueueChannel インスタンスの中間フローメッセージが残る可能性があります。これで、シャットダウンによりポーラーが実行されたままになり、これらのメッセージを排出して処理できるようになります。

統合グラフ

バージョン 4.3 から、Spring Integration はアプリケーションのランタイムオブジェクトモデルへのアクセスを提供します。これには、オプションでコンポーネントメトリクスを含めることができます。グラフとして公開され、統合アプリケーションの現在の状態を視覚化するために使用できます。o.s.i.support.management.graph パッケージには、Spring Integration コンポーネントのランタイム状態を単一のツリーのような Graph オブジェクトとして収集、構築、レンダリングするために必要なすべてのクラスが含まれています。IntegrationGraphServer は、Graph オブジェクトを作成、取得、リフレッシュするために Bean として宣言する必要があります。結果の Graph オブジェクトは任意の形式に直列化できますが、JSON はクライアント側で解析および表現するのに柔軟で便利です。デフォルトのコンポーネントのみを持つ Spring Integration アプリケーションは、次のようなグラフを公開します。

{
  "contentDescriptor" : {
    "providerVersion" : "5.3.0.RELEASE",
    "providerFormatVersion" : 1.2,
    "provider" : "spring-integration",
    "name" : "myAppName:1.0"
  },
  "nodes" : [ {
    "nodeId" : 1,
    "componentType" : "null-channel",
    "integrationPatternType" : "null_channel",
    "integrationPatternCategory" : "messaging_channel",
    "properties" : { },
    "sendTimers" : {
      "successes" : {
        "count" : 1,
        "mean" : 0.0,
        "max" : 0.0
      },
      "failures" : {
        "count" : 0,
        "mean" : 0.0,
        "max" : 0.0
      }
    },
    "receiveCounters" : {
      "successes" : 0,
      "failures" : 0
    },
    "name" : "nullChannel"
  }, {
    "nodeId" : 2,
    "componentType" : "publish-subscribe-channel",
    "integrationPatternType" : "publish_subscribe_channel",
    "integrationPatternCategory" : "messaging_channel",
    "properties" : { },
    "sendTimers" : {
      "successes" : {
        "count" : 1,
        "mean" : 7.807002,
        "max" : 7.807002
      },
      "failures" : {
        "count" : 0,
        "mean" : 0.0,
        "max" : 0.0
      }
    },
    "name" : "errorChannel"
  }, {
    "nodeId" : 3,
    "componentType" : "logging-channel-adapter",
    "integrationPatternType" : "outbound_channel_adapter",
    "integrationPatternCategory" : "messaging_endpoint",
    "properties" : { },
    "output" : null,
    "input" : "errorChannel",
    "sendTimers" : {
      "successes" : {
        "count" : 1,
        "mean" : 6.742722,
        "max" : 6.742722
      },
      "failures" : {
        "count" : 0,
        "mean" : 0.0,
        "max" : 0.0
      }
    },
    "name" : "errorLogger"
  } ],
  "links" : [ {
    "from" : 2,
    "to" : 3,
    "type" : "input"
  } ]
}
バージョン 5.2 では、メトリクス管理で説明したように、Micrometer メーターに代わってレガシーメトリクスが非推奨となりました。上には表示されていませんが、レガシーメトリクス(stats 子ノードの下)は引き続きグラフに表示されますが、追加の子ノード "deprecated" : "stats are deprecated in favor of sendTimers and receiveCounters" があります。

一部の JSON シリアライザーでは、いくつかの手法を使用して、レガシー統計の組み込みを抑制できます。たとえば、Jackson を使用すると、NullSerializer で構成された SimpleModule を ObjectMapper に登録できます。

objectMapper.registerModule(new SimpleModule()
        .addSerializer(IntegrationNode.Stats.class, NullSerializer.instance));

結果の json には "stats" : null が含まれます。

前の例では、グラフは 3 つの最上位要素で構成されています。

contentDescriptor グラフ要素には、データを提供するアプリケーションに関する一般情報が含まれています。name は、IntegrationGraphServer Bean または spring.application.name アプリケーションコンテキスト環境プロパティでカスタマイズできます。他のプロパティはフレームワークによって提供され、同様のモデルを他のソースと区別できます。

links グラフ要素は、nodes グラフ要素のノード間の接続を表します。ソース Spring Integration アプリケーションの統合コンポーネント間の接続を表します。例: MessageChannel から MessageHandler を含む EventDrivenConsumer へ、または AbstractReplyProducingMessageHandler から MessageChannel へ。便宜上、リンクの目的を判断できるように、モデルには type 属性が含まれています。可能な型は次のとおりです。

  • inputMessageChannel からエンドポイント、inputChannelrequestChannel プロパティへの方向を識別します

  • outputMessageHandlerMessageProducerSourcePollingChannelAdapter から outputChannel または replyChannel プロパティを介した MessageChannel への方向

  • errorPollingConsumer または MessageProducer または SourcePollingChannelAdapter 上の MessageHandler から errorChannel プロパティを介して MessageChannel へ。

  • discarderrorChannel プロパティを介して DiscardingMessageHandler (MessageFilter など)から MessageChannel へ。

  • routeAbstractMappingMessageRouter (HeaderValueRouter など)から MessageChannel へ。output に似ていますが、実行時に決定されます。構成されたチャネルマッピングまたは動的に解決されたチャネルの場合があります。通常、ルーターはこの目的のために最大 100 個の動的ルートのみを保持しますが、dynamicChannelLimit プロパティを設定することでこの値を変更できます。

この要素からの情報を視覚化ツールで使用して、nodes グラフ要素のノード間の接続をレンダリングできます。from および to 番号は、リンクされたノードの nodeId プロパティの値を表します。例: link 要素を使用して、ターゲットノードで適切な port を決定できます。

次の「テキストイメージ」は、型間の関連を示しています。

              +---(discard)
              |
         +----o----+
         |         |
         |         |
         |         |
(input)--o         o---(output)
         |         |
         |         |
         |         |
         +----o----+
              |
              +---(error)

nodes グラフ要素は、おそらく最も興味深いものです。その要素には、componentType インスタンスと name 値を持つランタイムコンポーネントが含まれるだけでなく、オプションでコンポーネントによって公開されるメトリクスも含まれる可能性があるためです。ノード要素には、一般的に一目でわかるさまざまなプロパティが含まれています。例: 式ベースのコンポーネントには、コンポーネントの 1 次式文字列を含む expression プロパティが含まれます。メトリクスを有効にするには、@EnableIntegrationManagement を @Configuration クラスに追加するか、<int:management/> 要素を XML 構成に追加します。詳細については、指標と管理を参照してください。

nodeId は、あるコンポーネントと別のコンポーネントを区別できるようにする一意の増分識別子を表します。links 要素でも使用され、このコンポーネントと他のコンポーネント(ある場合)との関連(接続)を表します。input および output 属性は、AbstractEndpointMessageHandlerSourcePollingChannelAdapter または MessageProducerSupport の inputChannel および outputChannel プロパティ用です。詳細については、次のセクションを参照してください。

バージョン 5.1 以降、IntegrationGraphServer は、特定の NamedComponent の IntegrationNode 上の追加プロパティの作成に Function<NamedComponent, Map<String, Object>> additionalPropertiesCallback を受け入れます。たとえば、SmartLifecycle autoStartuprunning プロパティをターゲットグラフに公開できます。

server.setAdditionalPropertiesCallback(namedComponent -> {
            Map<String, Object> properties = null;
            if (namedComponent instanceof SmartLifecycle) {
                SmartLifecycle smartLifecycle = (SmartLifecycle) namedComponent;
                properties = new HashMap<>();
                properties.put("auto-startup", smartLifecycle.isAutoStartup());
                properties.put("running", smartLifecycle.isRunning());
            }
            return properties;
        });

グラフランタイムモデル

Spring Integration コンポーネントには、さまざまなレベルの複雑さがあります。例: ポーリングされた MessageSource には、ソースデータからメッセージを定期的に送信する SourcePollingChannelAdapter と MessageChannel もあります。他のコンポーネントは、メッセージ用の requestChannel (input)にサブスクライブ(またはポーリング)するための消費 AbstractEndpoint と、ダウンストリームに送信するための応答メッセージを生成する replyChannel (output)を備えたミドルウェアのリクエスト / 応答コンポーネント(JmsOutboundGateway など)です。一方、MessageProducerSupport 実装(ApplicationEventListeningMessageProducer など)は、一部のソースプロトコルリスニングロジックをラップし、outputChannel にメッセージを送信します。

グラフ内で、Spring Integration コンポーネントは、IntegrationNode クラス階層を使用して表されます。これは、o.s.i.support.management.graph パッケージに含まれています。例: AggregatingMessageHandler には ErrorCapableDiscardingMessageHandlerNode を使用でき(discardChannel オプションがあるため)、PollingConsumer を使用して PollableChannel からコンシュームするときにエラーを生成できます。もう 1 つの例は CompositeMessageHandlerNode です。EventDrivenConsumer を使用して SubscribableChannel にサブスクライブした場合の MessageHandlerChain 用です。

@MessagingGateway (メッセージングゲートウェイを参照)は、各メソッドにノードを提供します。name 属性は、ゲートウェイの Bean 名と短いメソッドシグネチャーに基づいています。ゲートウェイの次の例を検討してください。
@MessagingGateway(defaultRequestChannel = "four")
public interface Gate {

	void foo(String foo);

	void foo(Integer foo);

	void bar(String bar);

}

前述のゲートウェイは、次のようなノードを生成します。

{
  "nodeId" : 10,
  "name" : "gate.bar(class java.lang.String)",
  "stats" : null,
  "componentType" : "gateway",
  "integrationPatternType" : "gateway",
  "integrationPatternCategory" : "messaging_endpoint",
  "output" : "four",
  "errors" : null
},
{
  "nodeId" : 11,
  "name" : "gate.foo(class java.lang.String)",
  "stats" : null,
  "componentType" : "gateway",
  "integrationPatternType" : "gateway",
  "integrationPatternCategory" : "messaging_endpoint",
  "output" : "four",
  "errors" : null
},
{
  "nodeId" : 12,
  "name" : "gate.foo(class java.lang.Integer)",
  "stats" : null,
  "componentType" : "gateway",
  "integrationPatternType" : "gateway",
  "integrationPatternCategory" : "messaging_endpoint",
  "output" : "four",
  "errors" : null
}

この IntegrationNode 階層を使用して、クライアント側でグラフモデルを解析したり、一般的な Spring Integration ランタイムの動作を理解したりできます。詳細については、プログラミングのヒントとコツも参照してください。

バージョン 5.3 は、IntegrationPattern 抽象化と、エンタープライズ統合パターン(EIP)を表すすべての標準コンポーネントを導入し、この抽象化を実装し、IntegrationPatternType 列挙値を提供します。この情報は、ターゲットアプリケーションのいくつかの分類ロジックに役立ちます。または、グラフノードに公開されているため、UI がコンポーネントの描画方法を決定するために使用できます。

統合グラフコントローラー

アプリケーションが Web ベース(または Web コンテナーが埋め込まれた Spring Boot 上に構築されている)であり、Spring Integration HTTP または WebFlux モジュール(それぞれ HTTP サポートおよび WebFlux サポートを参照)がクラスパスに存在する場合、IntegrationGraphController を使用して IntegrationGraphServer を公開できます。REST サービスとしての機能。この目的のために、@EnableIntegrationGraphController および @Configuration クラスのアノテーションと <int-http:graph-controller/> XML 要素が HTTP モジュールで使用可能です。この構成は、@EnableWebMvc アノテーション(または XML 定義の場合は <mvc:annotation-driven/>)とともに、IntegrationGraphController @RestController を登録します。この構成では、@RequestMapping.path を @EnableIntegrationGraphController アノテーションまたは <int-http:graph-controller/> 要素で構成できます。デフォルトのパスは /integration です。

IntegrationGraphController @RestController は、次のサービスを提供します。

  • @GetMapping(name = "getGraph"): 最後の IntegrationGraphServer リフレッシュ以降の Spring Integration コンポーネントの状態を取得します。o.s.i.support.management.graph.Graph は、REST サービスの @ResponseBody として返されます。

  • @GetMapping(path = "/refresh", name = "refreshGraph"): 実際のランタイム状態の現在の Graph をリフレッシュし、REST レスポンスとして返します。メトリクスのグラフをリフレッシュする必要はありません。グラフが取得されると、リアルタイムで提供されます。グラフが最後に取得されてからアプリケーションコンテキストが変更された場合、リフレッシュを呼び出すことができます。その場合、グラフは完全に再構築されます。

Spring Security および Spring MVC プロジェクトによって提供される標準の構成オプションとコンポーネントを使用して、IntegrationGraphController のセキュリティとクロスオリジンの制限を設定できます。次の例は、これらのゴールを達成します。

<mvc:annotation-driven />

<mvc:cors>
	<mvc:mapping path="/myIntegration/**"
				 allowed-origins="http://localhost:9090"
				 allowed-methods="GET" />
</mvc:cors>

<security:http>
    <security:intercept-url pattern="/myIntegration/**" access="ROLE_ADMIN" />
</security:http>


<int-http:graph-controller path="/myIntegration" />

次の例は、Java 構成で同じことを行う方法を示しています。

@Configuration
@EnableWebMvc // or @EnableWebFlux
@EnableWebSecurity // or @EnableWebFluxSecurity
@EnableIntegration
@EnableIntegrationGraphController(path = "/testIntegration", allowedOrigins="http://localhost:9090")
public class IntegrationConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
	    http
            .authorizeRequests()
               .antMatchers("/testIntegration/**").hasRole("ADMIN")
            // ...
            .formLogin();
    }

    //...

}

便宜上、@EnableIntegrationGraphController アノテーションは allowedOrigins 属性を提供することに注意してください。これにより、path への GET アクセスが提供されます。より高度にするために、標準の Spring MVC メカニズムを使用して CORS マッピングを構成できます。