路由单
从 4.1 版本开始,Spring Integration 提供了 路由单 企业集成模式的实现。
它被实现为一个 routingSlip
消息头,当端点未指定 outputChannel
时,该消息头用于确定 AbstractMessageProducingHandler
实例中的下一个通道。
此模式在复杂、动态的场景中非常有用,此时配置多个路由器以确定消息流可能会变得困难。
当消息到达没有 output-channel
的端点时,会查询 routingSlip
以确定消息要发送到的下一个通道。
当路由单用尽时,正常的 replyChannel
处理将恢复。
路由单的配置以 HeaderEnricher
选项的形式呈现——一个包含 path
条目的分号分隔的路由单,示例如下:
<util:properties id="properties">
<beans:prop key="myRoutePath1">channel1</beans:prop>
<beans:prop key="myRoutePath2">request.headers[myRoutingSlipChannel]</beans:prop>
</util:properties>
<context:property-placeholder properties-ref="properties"/>
<header-enricher input-channel="input" output-channel="process">
<routing-slip
value="${myRoutePath1}; @routingSlipRoutingPojo.get(request, reply);
routingSlipRoutingStrategy; ${myRoutePath2}; finishChannel"/>
</header-enricher>
前面的示例包含:
-
<context:property-placeholder>
配置,用于演示路由单path
中的条目可以指定为可解析的键。 -
<header-enricher>
的<routing-slip>
子元素用于将RoutingSlipHeaderValueMessageProcessor
填充到HeaderEnricher
处理器。 -
RoutingSlipHeaderValueMessageProcessor
接受已解析的路由单path
条目的String
数组,并返回(从processMessage()
)一个singletonMap
,其中path
作为key
,0
作为初始routingSlipIndex
。
路由单 path
条目可以包含 MessageChannel
bean 名称、RoutingSlipRouteStrategy
bean 名称和 Spring 表达式 (SpEL)。
RoutingSlipHeaderValueMessageProcessor
在第一次 processMessage
调用时,对照 BeanFactory
检查每个路由单 path
条目。
它将(在应用程序上下文中不是 bean 名称的)条目转换为 ExpressionEvaluatingRoutingSlipRouteStrategy
实例。
RoutingSlipRouteStrategy
条目会被多次调用,直到它们返回 null 或空 String
。
由于路由单参与 getOutputChannel
过程,我们有一个请求-回复上下文。
引入了 RoutingSlipRouteStrategy
来确定使用 requestMessage
和 reply
对象的下一个 outputChannel
。
此策略的实现应在应用程序上下文中注册为 bean,其 bean 名称在路由单 path
中使用。
提供了 ExpressionEvaluatingRoutingSlipRouteStrategy
实现。
它接受一个 SpEL 表达式,并使用内部 ExpressionEvaluatingRoutingSlipRouteStrategy.RequestAndReply
对象作为评估上下文的根对象。
这是为了避免每次 ExpressionEvaluatingRoutingSlipRouteStrategy.getNextPath()
调用时创建 EvaluationContext
的开销。
它是一个简单的 Java bean,具有两个属性:Message<?> request
和 Object reply
。
通过此表达式实现,我们可以使用 SpEL(例如,@routingSlipRoutingPojo.get(request, reply)
和 request.headers[myRoutingSlipChannel]
)指定路由单 path
条目,并避免为 RoutingSlipRouteStrategy
定义 bean。
|
如果路由单涉及分布式环境,我们建议不要将内联表达式用于路由单 path
。
此建议适用于分布式环境,例如跨 JVM 应用程序,通过消息代理(例如 AMQP 支持 或 JMS 支持)使用 request-reply
,或在集成流中使用持久 MessageStore
(消息存储)。
框架使用 RoutingSlipHeaderValueMessageProcessor
将它们转换为 ExpressionEvaluatingRoutingSlipRouteStrategy
对象,并在 routingSlip
消息头中使用它们。
由于此类的不可序列化(它不能是,因为它依赖于 BeanFactory
),整个 Message
变得不可序列化,并且在任何分布式操作中,我们最终都会遇到 NotSerializableException
。
为了克服此限制,请注册一个具有所需 SpEL 的 ExpressionEvaluatingRoutingSlipRouteStrategy
bean,并在路由单 path
配置中使用其 bean 名称。
对于 Java 配置,您可以将 RoutingSlipHeaderValueMessageProcessor
实例添加到 HeaderEnricher
bean 定义中,示例如下:
@Bean
@Transformer(inputChannel = "routingSlipHeaderChannel")
public HeaderEnricher headerEnricher() {
return new HeaderEnricher(Collections.singletonMap(IntegrationMessageHeaderAccessor.ROUTING_SLIP,
new RoutingSlipHeaderValueMessageProcessor("myRoutePath1",
"@routingSlipRoutingPojo.get(request, reply)",
"routingSlipRoutingStrategy",
"request.headers[myRoutingSlipChannel]",
"finishChannel")));
}
当端点生成回复且未定义 outputChannel
时,路由单算法的工作方式如下:
-
routingSlipIndex
用于从路由单path
列表中获取值。 -
如果
routingSlipIndex
的值是String
,则它用于从BeanFactory
获取 bean。 -
如果返回的 bean 是
MessageChannel
的实例,则它用作下一个outputChannel
,并且routingSlipIndex
在回复消息头中递增(路由单path
条目保持不变)。 -
如果返回的 bean 是
RoutingSlipRouteStrategy
的实例,并且其getNextPath
不返回空String
,则该结果用作下一个outputChannel
的 bean 名称。routingSlipIndex
保持不变。 -
如果
RoutingSlipRouteStrategy.getNextPath
返回空String
或null
,则routingSlipIndex
递增,并递归调用getOutputChannelFromRoutingSlip
以获取下一个路由单path
项。 -
如果下一个路由单
path
条目不是String
,则它必须是RoutingSlipRouteStrategy
的实例。 -
当
routingSlipIndex
超过路由单path
列表的大小时,算法将移动到标准replyChannel
头的默认行为。