路由单
从 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头的默认行为。