路由單
從版本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頭的預設行為。