JMX 支持
Spring Integration 提供了通道适配器,用于接收和发布 JMX 通知。
你需要将这个依赖添加到你的项目中:
- Maven
- Gradle
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-jmx</artifactId>
    <version>6.4.2</version>
</dependency>
compile "org.springframework.integration:spring-integration-jmx:6.4.2"
入站通道适配器允许轮询 JMX MBean 属性值,而出站通道适配器允许调用 JMX MBean 操作。
通知监听通道适配器
通知监听通道适配器需要一个 JMX ObjectName,用于发布通知的 MBean,此监听器应注册到该 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"/>
_Notification-listening 通道适配器是事件驱动的,并直接注册到 MBeanServer。它不需要任何轮询器配置。
对于此组件,object-name 属性可以包含对象名称模式(例如,"org.something:type=MyType,name=*")。在这种情况下,适配器会从所有与模式匹配的对象名称的 MBeans 接收通知。此外,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-name 和 attribute-name 是必需的。还需要一个 MBeanServer 引用。但是,默认情况下,它会自动检查名为 mbeanServer 的 bean,这与通知监听通道适配器 前面描述的 一样。以下示例展示了如何使用 XML 配置属性轮询通道适配器:
<int-jmx:attribute-polling-channel-adapter 
    object-name="domain:type=Type,name=Name" 
    attribute-name="AttributeName" 
    mbean-server="mbeanServer" 
    channel="outputChannel"/>
请注意,上述代码中的 mbeanServer 和 outputChannel 需要根据实际情况进行定义。
<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 树并发送消息,消息的有效负载是与查询匹配的对象图。默认情况下,MBeans 被映射到原始类型和简单对象,例如 Map、List 和数组。这样做允许简单地转换为(例如)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>
前面的示例包含了选定 MBeans 上的所有属性。你可以通过提供一个配置了适当过滤器的 MBeanObjectConverter 来过滤属性。你可以通过使用 converter 属性,将转换器作为对 bean 定义的引用提供,或者可以使用内嵌的 <bean/> 定义。Spring Integration 提供了一个 DefaultMBeanObjectConverter,它可以在构造函数参数中接收一个 MBeanAttributeFilter。
Spring Integration 提供了两个标准过滤器。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 属性。
消息的有效负载被映射到操作的参数,如果有。Map 类型的有效负载带有 String 键被视为名称/值对,而 List 或数组则作为简单的参数列表传递(没有明确的参数名称)。如果操作需要一个参数值,有效负载可以表示那个单个值。另外,如果操作不需要任何参数,则有效负载将被忽略。
如果你想暴露一个通道,用于通过不需要包含标题的消息调用单个通用操作,那么最后一个选项效果很好。
操作调用 outbound 网关
类似于操作调用通道适配器,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 组件本身可以被暴露为 MBeans。要创建 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 bean,该 bean 的类型为 IntegrationMBeanExporter,并带有几个有用的选项。以下示例展示了如何配置此 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 对象名称
应用程序中的所有 MessageChannel、MessageHandler 和 MessageSource 实例都由 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> | 
请注意,表格中的 <channelName> 和 <source> 是占位符,表示具体的通道名称和源名称。
对象名称中的 bean 属性针对来源和处理程序取下表中的一个值:
表 2. bean 对象名称部分
| 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 。这样做可以对 MBeans 的命名进行更大的控制,例如将所有集成 MBeans 组合到一个名为 '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 MBeans:`my.domain:type=Integration,*`。这样做还可以在如 VisualVM 等工具中的域下,将这些 beans 归类到一个树形条目中。
默认的命名策略是 MetadataNamingStrategy。导出器将 default-domain 传播给该对象,以便在解析 bean 键失败时生成一个回退对象名称。如果您的自定义命名策略是 MetadataNamingStrategy(或其子类),导出器不会传播 default-domain。您必须在策略 bean 上配置它。
从 5.1 版本开始,任何包含不允许在 Java 标识符中出现的字符(或句号 .)的 bean 名称(由对象名称中的 name 键表示)都将被引用。
JMX 改进
版本 4.2 引入了一些重要的改进,对框架中的 JMX 支持进行了相当大的改造。这些改进显著提高了 JMX 统计信息收集的性能,并提供了更多的控制。然而,在一些特定(不常见)的情况下,它对用户代码有一些影响。以下详细说明了这些更改,在必要时会提出警告。
@IntegrationManagedResource
类似于 @ManagedResource 注解,@IntegrationManagedResource 将一个类标记为有资格被导出为 MBean。但是,它只有在应用程序上下文中有一个 IntegrationMBeanExporter 时才会被导出。
某些 Spring Integration 类(在 org.springframework.integration 包中)之前被注解为 @ManagedResource,现在它们被同时注解为 @ManagedResource 和 @IntegrationManagedResource。这是为了向后兼容(请参阅下一项)。此类 MBeans 由任何上下文 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() 操作来注册和注销 MBeans。MBeans 现在在上下文初始化期间进行注册,并在上下文被销毁时注销。
有序关闭托管操作
MBean 导出器允许 JMX 操作以有序的方式关闭应用程序。它打算在停止 JVM 之前使用。以下示例显示了如何使用它:
public void stopActiveComponents(long howLong)
其使用和操作在 有序关机 中有描述。