|
对于最新稳定版本,请使用 Spring Integration 7.0.0! |
动态布线器
Spring Integration为常见的内容型路由场景提供了多种不同的路由器配置,并支持将自定义路由器作为POJO实现的选项。
例如有效载荷类型路由器提供了一种简单的方法来配置路由器,根据收到消息的有效载荷类型计算信道,且头值路由器在配置路由器时提供了同样的便利,该路由器通过计算特定消息头部的值来计算信道。
还有基于表达式(SpEL)的路由器,其通道是通过计算表达式确定的。
所有这些类型的布线器都表现出一些动态特性。
然而,这些路由器都需要静态配置。 即使是基于表达式的路由器,表达式本身也被定义为路由器配置的一部分,这意味着同一表达式在相同值下作时,总是计算出相同的通道。 这在大多数情况下是可以接受的,因为这些路线定义明确且可预测。 但有时我们需要动态更改路由器配置,以便消息流被路由到不同的频道。
例如,你可能想关闭系统的某部分进行维护,并暂时将消息重新路由到另一个消息流。
再举个例子,你可能想通过增加另一条路径来处理更具体的java.lang.Number(在有效载荷类型路由器).
不幸的是,静态路由器配置要实现这两个目标,你必须关闭整个应用,更改路由器配置(更改路由),然后再重新启用应用。 显然,这不是任何人想要的解决方案。
动态路由器模式描述了如何动态更改或配置路由器而不导致系统或单个路由器瘫痪的机制。
在我们具体介绍 Spring Integration 如何支持动态路由之前,我们需要先考虑路由器的典型流程:
-
计算通道标识符,这是路由器接收消息后计算的值。通常,它是一个字符串或实际消息的实例
消息频道. -
将通道标识符解析为通道名称。我们在本节后面会详细介绍该过程的具体内容。
-
将频道名称解析为实际
消息频道
如果第1步结果是实际的消息频道,因为消息频道是任何路由器作业的最终结果。然而,如果第一步产生了一个通道标识符,而不是消息频道,你有相当多种可能的方式影响推导消息频道. 请考虑以下有效载荷型路由器的示例:
<int:payload-type-router input-channel="routingChannel">
<int:mapping type="java.lang.String" channel="channel1" />
<int:mapping type="java.lang.Integer" channel="channel2" />
</int:payload-type-router>
在有效载荷型路由器的背景下,上述三个步骤具体实现如下:
-
计算一个通道标识符,即有效载荷类型的完全限定名称(例如,
java.lang.字符串). -
将通道标识符解析为通道名称,利用前一步的结果从定义的有效载荷类型映射中选择合适的值,
映射元素。 -
将通道名称解析为
消息频道作为应用上下文中的豆子的引用(希望是消息频道)由前一步的结果识别。
换句话说,每一步都会传递到下一步,直到过程完成。
现在考虑一个头值路由器的例子:
<int:header-value-router input-channel="inputChannel" header-name="testHeader">
<int:mapping value="foo" channel="fooChannel" />
<int:mapping value="bar" channel="barChannel" />
</int:header-value-router>
现在我们可以考虑头值路由器的三个步骤:
-
计算一个通道标识符,即由
首部名称属性。 -
将通道标识符解析为通道名称,利用前一步的结果从定义的通用映射中选择合适的值
映射元素。 -
将通道名称解析为
消息频道作为应用上下文中的豆子的引用(希望是消息频道)由前一步的结果识别。
前两种不同路由器类型的配置看起来几乎一模一样。然而,如果你看看头值路由器我们清楚地看到,没有映射子元素,如下列表所示:
<int:header-value-router input-channel="inputChannel" header-name="testHeader"/>
不过,这个配置依然完全有效。那么自然的问题是,第二步的映射怎么办?
第二步现在是可选的。 如果映射未定义,则第一步计算的通道标识符值自动被视为频道名称,现在被解析为实际的消息频道,如第三步。这也意味着第二步是为路由器提供动态特性的关键步骤之一,因为它引入了一个过程,可以更改通道标识符解析为通道名称的方式,从而影响确定最终实例的过程消息频道根据初始频道标识符。
例如,在上述配置中,假设测试头值为“kermit”,现在是通道标识符(第一步)。由于该路由器没有映射,无法将该通道标识符解析为通道名称(第二步),该通道标识符现在被视为通道名称。但是,如果有映射但值不同呢?最终结果仍然相同,因为如果通过将通道标识符解析为通道名称的过程无法确定新值,通道标识符就变成了通道名称。
剩下的就是第三步将通道名(“kermit”)解析为消息频道以此名称标识。这基本上涉及对所提供名称进行豆子查找。现在所有包含头值对的消息为testHeader=kermit将被引导到一个消息频道其豆名(其身份证)是“kermit”。
但如果你想把这些消息路由到“simpson”频道呢?显然,改变静态配置是可行的,但这样做也需要降低你的系统。不过,如果你已经访问了频道标识符映射,你可以引入一个新的映射,将头值对重新映射到Kermit=Simpson。,因此第二步将“kermit”作为通道标识符,同时将其解析为“simpson”作为通道名称。
显然,这同样适用于有效载荷类型路由器,现在你可以重新映射或移除特定类型的有效载荷映射。
事实上,它适用于所有其他路由器,包括基于表达式的路由器,因为它们计算出的值现在有机会经过第二步,最终被解析为实际频道名称.
任何属于摘要地图消息路由器(包括大多数框架定义的布线器)是动态布线器,因为频道映射定义在摘要地图消息路由器水平。
该地图的设置器方法作为公开方法暴露,与“setChannelMapping”和“removeChannelMapping”方法一起。
只要你有路由器本身的参考,这些工具允许你在运行时更改、添加和移除路由器映射。
这也意味着你可以通过 JMX(参见 JMX 支持)或 Spring Integration 控制总线(参见控制总线)功能来暴露这些相同的配置选项。
回归信道密钥,因为信道名称灵活且方便。
然而,如果你不信任消息创建者,恶意行为者(了解系统)可能会创建消息,并将其路由到意想不到的渠道。
例如,如果密钥设置为路由器输入通道的通道名称,这样的消息会被路由回路由器,最终导致堆栈溢出错误。
因此,您可能希望关闭此功能(设置channelKeyFallback属性到false),并在需要时更改映射。 |
使用控制总线管理路由器映射
管理路由器映射的一种方法是控制总线模式,它会暴露一个控制通道,你可以通过它发送控制消息,管理和监控 Spring Integration 组件,包括路由器。
| 有关控制总线的更多信息,请参见控制总线。 |
通常,你会发送一个控制消息,要求对某个受管理组件(例如路由器)调用特定作。 以下管理作(方法)专门用于更改路由器解析过程:
-
public void setChannelMapping(String key, String channelName): 允许你添加新的或修改现有映射信道标识符和频道名称 -
public void removeChannelMapping(String key)允许您移除特定的通道映射,从而切断信道标识符和频道名称
请注意,这些方法可用于简单的更改(例如更新单一路由或添加或删除一条路由)。 但是,如果你想移除一条路由并添加另一条,更新不是原子式的。 这意味着路由表在更新之间可能处于不确定状态。 从4.0版本开始,你现在可以用控制总线原子更新整个路由表。 以下方法可以让你做到这一点:
-
public Map<String, String>getChannelMappings()返回当前映射。 -
公共空置替换通道映射(属性通道映射): 更新映射。 注意频道映射参数是性能对象。 这种布局使控制总线命令能够使用内置的StringToProperties转换器,如下示例所示:
"@'router.handler'.replaceChannelMappings('foo=qux \n baz=bar')"
注意每个映射之间都用一个换行字符分隔(\n).
对于对地图进行程序性更改,我们建议您使用setChannelMappings由于类型安全考虑,方法。replaceChannelMappings忽略不存在的键或值字符串对象。
通过使用 JMX 管理路由器映射
你也可以使用 Spring 的 JMX 支持来暴露一个路由器实例,然后用你喜欢的 JMX 客户端(例如 JConsole)来管理这些作(方法),以便更改路由器配置。
| 有关 Spring Integration 的 JMX 支持的更多信息,请参见 JMX 支持。 |