JMX 支持

Spring Integration 提供通道适配器,用于接收和发布 JMX 通知。

您需要将此依赖项包含到您的项目中

  • Maven

  • Gradle

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

入站通道适配器允许轮询 JMX MBean 属性值,而出站通道适配器允许调用 JMX MBean 操作。

监听通知的通道适配器

监听通知的通道适配器需要一个 JMX ObjectName,用于发布此监听器应注册的通知的 MBean。一个非常简单的配置可能类似于以下内容

<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"/>

_监听通知的通道适配器_ 是事件驱动的,并直接注册到 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。优先级始终赋予与 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-nameattribute-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 提供了一个 DefaultMBeanObjectConverter,它可以在其构造函数参数中接受一个 MBeanAttributeFilter

Spring Integration 提供了两种标准过滤器。NamedFieldsMBeanAttributeFilter 允许您指定要包含的属性列表。NotNamedFieldsMBeanAttributeFilter 允许您指定要排除的属性列表。您也可以实现自己的过滤器。

操作调用通道适配器

操作调用通道适配器支持由消息驱动的任何由 MBean 公开的受管操作的调用。每次调用都需要调用操作名称和目标 MBean 的对象名称。这两个都必须通过适配器配置或分别通过 JmxHeaders.OBJECT_NAMEJmxHeaders.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 类型有效负载被视为名称/值对,而 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 导出器

当配置了IntegrationMBeanExporter时,Spring Integration组件本身可以作为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引入了@EnableIntegrationMBeanExport注解,允许在@Configuration类级别方便地配置一个类型为IntegrationMBeanExporter的默认integrationMbeanExporter bean,该bean具有几个有用的选项。以下示例展示了如何配置此bean

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

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

如果您需要提供更多选项或拥有多个IntegrationMBeanExporter bean(例如,用于不同的MBean服务器或避免与标准Spring MBeanExporter冲突——例如,通过@EnableMBeanExport),您可以将IntegrationMBeanExporter配置为通用bean。

MBean对象名称

应用程序中的所有MessageChannelMessageHandlerMessageSource实例都被MBean导出器包装,以提供管理和监控功能。每个组件类型的生成的JMX对象名称列在下面的表格中

表 1. 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属性采用以下表格中的一个值

表 2. bean 对象名称部分
Bean值 描述

endpoint

封闭端点的bean名称(例如<service-activator>),如果有的话

anonymous

表示封闭端点没有用户指定的bean名称,因此JMX名称是输入通道名称。

internal

用于众所周知的Spring Integration默认组件

handler/source

以上皆非。回退到正在监控的对象(处理程序或源)的toString()方法。

您可以通过在object-name-static-properties属性中提供对Properties对象的引用,将自定义元素追加到对象名称。

此外,从 Spring Integration 3.0 开始,您可以通过设置object-naming-strategy属性来使用自定义的ObjectNamingStrategy。这样做允许您更好地控制 MBean 的命名,例如将所有集成 MBean 分组到“Integration”类型下。以下示例展示了一种可能的自定义命名策略实现

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设置为“Integration”,从而能够在一个查询中选择所有 Integration MBean:my.domain:type=Integration,*。这样做还会将 bean 分组到域下的一棵树条目中,例如在 VisualVM 等工具中。

默认命名策略是MetadataNamingStrategy。导出器将default-domain传播到该对象,以便在解析 bean 密钥失败时生成回退对象名称。如果您的自定义命名策略是MetadataNamingStrategy(或其子类),则导出器不会传播default-domain。您必须在策略 bean 上配置它。

从 5.1 版本开始,任何 bean 名称(由对象名称中的name键表示)如果包含任何不允许在 Java 标识符(或句点.)中使用的字符,都将被引用。

JMX 改进

4.2 版本引入了一些重要的改进,代表着框架中 JMX 支持的相当大的改进。这些改进导致 JMX 统计信息收集的性能显着提高,并提供了更多控制。但是,在一些特定(不常见)情况下,它对用户代码有一些影响。这些更改将在下面详细说明,必要时会进行警告。

@IntegrationManagedResource

类似于@ManagedResource注释,@IntegrationManagedResource将一个类标记为可以导出为 MBean。但是,只有当应用程序上下文具有IntegrationMBeanExporter时,它才会被导出。

某些 Spring 集成类(在 `org.springframework.integration` 包中)之前使用 `@ManagedResource` 注解,现在同时使用 `@ManagedResource` 和 `@IntegrationManagedResource` 注解。这是为了向后兼容(参见下一项)。这些 MBean 由任何上下文 `MBeanServer` 或 `IntegrationMBeanExporter` 导出(但不会同时导出 - 如果两个导出器都存在,则如果 Bean 匹配 `managed-components` 模式,则该 Bean 将由集成导出器导出)。

MBean 导出器 Bean 名称模式

以前,`managed-components` 模式仅是包含性的。如果 Bean 名称与其中一个模式匹配,它将被包含。现在,模式可以通过在前面加上 `!` 来否定。例如,`!thing*, things` 匹配所有不以 `thing` 开头的 Bean 名称,除了 `things`。模式从左到右进行评估。第一个匹配(正或负)获胜,然后不再应用其他模式。

在模式中添加此语法会导致一个可能(尽管可能不太可能)的问题。如果您有一个名为 `"!thing"` 的 Bean,并且您在 MBean 导出器的 `managed-components` 模式中包含了 `!thing` 模式,它将不再匹配;该模式现在匹配所有不名为 `thing` 的 Bean。在这种情况下,您可以在模式中使用 `\` 来转义 `!`。`\!thing` 模式匹配名为 `!thing` 的 Bean。
IntegrationMBeanExporter 变更

`IntegrationMBeanExporter` 不再实现 `SmartLifecycle`。这意味着 `start()` 和 `stop()` 操作不再可用以注册和注销 MBean。MBean 现在在上下文初始化期间注册,并在上下文销毁时注销。

有序关闭管理操作

MBean 导出器允许 JMX 操作以有序方式关闭应用程序。它旨在在停止 JVM 之前使用。以下示例展示了如何使用它

public void stopActiveComponents(long howLong)

它的使用和操作在 有序关闭 中描述。