# JMX 支持

# JMX 支持

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

你需要在项目中包含此依赖项:

Maven

<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-jmx</artifactId>
    <version>5.5.9</version>
</dependency>

Gradle

compile "org.springframework.integration:spring-integration-jmx:5.5.9"

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

# 通知-监听通道适配器

通知监听通道适配器需要一个 JMX,用于发布此侦听器应注册到的通知的 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注册。它不需要任何 Poller 配置。

仅对于这个组件,object-name属性可以包含一个对象名模式(例如,
“org.something:type=mytype,name=*”)。,在这种情况下,适配器接收来自所有具有匹配该模式的对象名的 MBean 的通知。,此外,
object-name属性可以包含对<util:list>对象名称模式的 SPEL 引用,如下例所示:

<br/><jmx:notification-listening-channel-adapter id="manyNotificationsAdapter"<br/> channel="manyNotificationsChannel"<br/> object-name="#{patterns}"/><br/><br/><util:list id="patterns"><br/> <value>org.foo:type=Foo,name=*</value><br/> <value>org.foo:type=Bar,name=*</value><br/></util:list><br/>
启用调试级别日志时,将记录定位的 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键关联的消息标头值。或者,你可以在配置中提供一个 fallbackdefault-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 集成中的任何其他轮询适配器相同的方式配置 Poller(或者可以依赖默认的 Poller)。需要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 集成提供了一个DefaultMBeanObjectConverter,它可以在其构造函数参数中接受一个MBeanAttributeFilter

Spring 集成提供了两个标准过滤器。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 与操作调用通道适配器类似,集成还提供了一个操作调用出站网关,当需要返回值时,可以在处理非 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 集成消息并将其直接发送到通道来启动的,则必须显式地指定消息头,或者使用reply-channel属性。

# MBean 出口商

Spring 当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 Core 中提供的输出器是正交的。
它注册消息通道和消息处理程序,但不注册本身。
通过使用标准<context:mbean-export/>标记,可以公开导出程序本身(以及 Spring 集成中的某些其他组件),
导出程序附带了一些度量指标,例如,计算处理程序的数量和排队消息的数量。

它还具有一个有用的操作,如有序停机管理运行中所讨论的。

Spring Integration4.0 引入了@EnableIntegrationMBeanExport注释,以允许方便地配置类型integrationMbeanExporter Bean 的默认IntegrationMBeanExporter@Configuration类级别的几个有用选项。下面的示例展示了如何配置这个 Bean:

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

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

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

# MBean 对象名

应用程序中的所有MessageChannelMessageHandlerMessageSource实例都由 MBean Exporter 包装,以提供管理和监视功能。下表列出了为每个组件类型生成的 JMX 对象名:

Component Type 对象名称
MessageChannel <br/> `o.s.i:type=MessageChannel,name=<channelName>`<br/>
MessageSource <br/> `o.s.i:type=MessageSource,name=<channelName>,bean=<source>`<br/>
MessageHandler <br/> `o.s.i:type=MessageSource,name=<channelName>,bean=<source>`<br/>

源和处理程序的对象名中的bean属性接受下表中的一个值:

Bean Value 说明
endpoint Bean 封闭端点的名称(例如<service-activator>),如果有一个
anonymous Bean 表示封闭端点没有用户指定的名称,因此 JMX 名称是输入通道名称。
internal 对于众所周知的 Spring 集成缺省组件
handler/source 以上都不是。
返回被监视对象的toString()方法(处理程序或源)

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

此外,由于 Spring Integration3.0,你可以通过设置ObjectNamingStrategy属性来使用自定义[ObjectNamingStrategy](https://DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/org/springframework/jmx/export/naming/objectnamingstrategy.html)。这样做可以对 MBean 的命名进行更大的控制,例如将所有集成 MBean 分组为“集成”类型。下面的示例展示了一种可能的自定义命名策略实现:

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

默认的命名策略是[MetadataNamingStrategy](https://DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/org/springframework/jmx/export/naming/metadatanamingstrategy.html)。
导出人员将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 通过任何上下文MBeanServerIntegrationMBeanExporter导出(但不是两者都导出——如果两个出口商都存在,则如果 Bean 匹配managed-components模式,则 Bean 由集成出口商导出)。

MBean Exporter Bean 名称模式

以前,managed-components模式仅包含。如果 Bean 名称与其中一种模式相匹配,则该名称将被包括在内。现在,可以通过在模式前加上!来否定该模式。例如,!thing*, things匹配所有不以thing开头的 Bean 名称,但things除外。模式是从左到右计算的。第一场比赛(正面或负面)获胜,然后不再应用其他模式。

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

整合-------------------------------------------------------

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

# 有序关停托管运行

MBean Exporter 允许一个 JMX 操作以有序的方式关闭应用程序。它是在停止 JVM 之前使用的。下面的示例展示了如何使用它:

public void stopActiveComponents(long howLong)

其用途和操作在有序关机中进行了说明。