路由单
从版本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())一个带有path作为key和0作为初始routingSlipIndex的singletonMap。
路由表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指定路由表path条目(例如,@routingSlipRoutingPojo.get(request, reply)和request.headers[myRoutingSlipChannel]),并避免为RoutingSlipRouteStrategy定义bean。
requestMessage参数始终是一个Message<?>。根据上下文,回复对象可能是Message<?>、AbstractIntegrationMessageBuilder或任意应用程序域对象(例如,当它由服务激活器调用的POJO方法返回时)。在前两种情况下,使用SpEL(或Java实现)时,通常的Message属性(payload和headers)是可用的。对于任意域对象,这些属性不可用。因此,如果结果用于确定下一个路径,在使用路由表与POJO方法结合时要小心。 |
如果在分布式环境中涉及路由表,我们建议不要在路由表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头的默认行为。