JMX 支持

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

您需要在项目中包含此依赖项

  • Maven

  • Gradle

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

入站通道适配器允许轮询 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 的引用和一个“回执(handback)”对象,以提供随每个通知一起传递的上下文。这两个属性都是可选的。将上述示例扩展以包含这些属性以及显式的 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 属性可以包含一个 SpEL 引用,指向一个 <util:list> 类型的对象名称模式列表,示例如下:

<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 属性。

消息的负载将被映射到操作的参数(如果存在)。一个键为 StringMap 类型负载被视为名称/值对,而 List 或数组则作为简单的参数列表传递(不带显式参数名称)。如果操作只需要一个参数值,负载可以代表该单个值。此外,如果操作不需要参数,则负载将被忽略。

如果您想为需要不包含头部的消息暴露一个用于单一常见操作调用的通道,则最后一种选项效果很好。

操作调用出站网关

与操作调用通道适配器类似,Spring Integration 也提供了一个操作调用出站网关,当您处理需要返回值 的非空操作时可以使用它。返回值作为消息负载发送到网关指定的 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 Exporter

当配置了 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 exporter 与 Spring core 中提供的 exporter 是正交的。它注册消息通道和消息处理程序,但不注册自身。您可以使用标准的 <context:mbean-export/> 标签来暴露 exporter 本身(以及 Spring Integration 中的某些其他组件)。此 exporter 附带了一些指标——例如,处理程序数量和队列中消息数量的计数。

它还有一个有用的操作,如有序关闭管理操作中所述。

Spring Integration 4.0 引入了 @EnableIntegrationMBeanExport 注解,以便在 @Configuration 类级别方便地配置一个默认的类型为 IntegrationMBeanExporterintegrationMbeanExporter 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 exporter 包装,以提供管理和监控功能。下表列出了每种组件类型生成的 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 ObjectName 部分
Bean 值 描述

endpoint

包含端点(例如 <service-activator>)的 bean 名称(如果存在)

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',从而可以通过一个查询选择所有集成 MBean:my.domain:type=Integration,*。这样做还可以将 bean 在 VisualVM 等工具的域下的一个树状条目下进行分组。

默认的命名策略是 MetadataNamingStrategy。如果解析 bean 键失败,exporter 会将 default-domain 传播到该对象,以便它生成一个备用的对象名称。如果您的自定义命名策略是 MetadataNamingStrategy(或其子类),exporter 不会传播 default-domain。您必须在您的策略 bean 上进行配置。

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

JMX 改进

版本 4.2 引入了一些重要的改进,代表了框架中 JMX 支持的相当大的改进。这些改进带来了 JMX 统计数据收集的显著性能提升,并提供了更多的控制。然而,在少数特定(不常见)情况下,它对用户代码有一些影响。这些变更在下面进行了详细说明,必要时会给出警告。

@IntegrationManagedResource

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

Spring Integration 中(在 org.springframework.integration 包中)的某些类以前使用 @ManagedResource 注解,现在同时使用 @ManagedResource@IntegrationManagedResource 注解。这是为了向后兼容(参见下一项)。这些 MBean 可以由任何上下文的 MBeanServerIntegrationMBeanExporter 导出(但不能同时由两者导出——如果两者都存在,如果 bean 与 managed-components 模式匹配,则由集成 exporter 导出)。

MBean Exporter Bean 名称模式

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

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

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

有序关闭管理操作

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

public void stopActiveComponents(long howLong)

其用法和操作在有序关闭中描述。