路由滑块
从 4.1 版本开始,Spring Integration 提供了 路由单 企业集成模式的实现。它被实现为一个 routingSlip
消息头,用于在 AbstractMessageProducingHandler
实例中确定下一个通道,当端点没有指定 outputChannel
时。这种模式在复杂、动态的情况下很有用,在这种情况下,配置多个路由器来确定消息流可能会变得很困难。当消息到达没有 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
被引入来确定下一个outputChannel
,该outputChannel
使用requestMessage
和reply
对象。此策略的实现应该在应用程序上下文中注册为一个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` 消息头中使用它们。由于此类不是 `Serializable`(它不能是,因为它依赖于 `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` 头的默认行为。