消息筛选器
为了实现基于内容的路由,路由服务使用 MessageFilter 实现,这些实现检查消息的特定部分,例如地址、终结点名称或特定 XPath 语句。 如果随 .NET Framework 4.6.1 一起提供的消息筛选器均无法满足需求,你可以通过创建 MessageFilter 基类的新实现来创建自定义筛选器。
配置路由服务时,必须定义用于描述 MessageFilter 的类型的筛选器元素(FilterElement 对象),以及创建筛选器所需的任何支持数据(例如,要在消息中搜索的特定字符串值)。 请注意,创建筛选器元素仅定义了单独的消息筛选器;若要使用筛选器计算和路由消息,还必须定义筛选器表 (FilterTableEntryCollection)。
筛选器表中的每个条目都引用一个筛选器元素,并指定当消息与筛选器匹配时消息将路由到的客户端终结点。 通过筛选器表条目,您还可以指定一个备份终结点集合 (BackupEndpointCollection),该集合定义在向主终结点发送消息但出现传输故障时,该消息将传输到的目标终结点的列表。 将按指定顺序尝试这些终结点,直至某个终结点成功为止。
消息筛选器
路由服务使用的消息筛选器提供常用消息选择功能,例如,计算消息已发送到的终结点的名称、SOAP 操作或消息已发送到的地址或地址前缀。 也可以使用 AND
条件连接筛选器,这样,仅当消息同时与两个筛选器匹配时,才会将消息路由至某个终结点。 此外,还可以通过创建您自己的 MessageFilter 实现来创建自定义筛选器。
下表列出了路由服务使用的 FilterType、实现特定消息筛选器的类,以及所需的 FilterData 参数。
筛选器类型 | 说明 | 筛选器数据含义 | 示例筛选器 |
---|---|---|---|
操作 | 使用 ActionMessageFilter 类匹配包含特定操作的消息。 | 筛选器基于的操作。 | <filter name="action1" filterType="Action" filterData="http://namespace/contract/operation" /> |
EndpointAddress | 使用 EndpointAddressMessageFilter 类 (IncludeHostNameInComparison == true ) 匹配包含特定地址的消息。 |
筛选器基于的地址(在 To 标头中)。 | <filter name="address1" filterType="EndpointAddress" filterData="http://host/vdir/s.svc/b" /> |
EndpointAddressPrefix | 使用 PrefixEndpointAddressMessageFilter 类 (IncludeHostNameInComparison == true ) 匹配包含特定地址前缀的消息。 |
筛选器基于的地址(使用最长的前缀匹配项)。 | <filter name="prefix1" filterType="EndpointAddressPrefix" filterData="http://host/" /> |
且 | 使用始终在返回前计算两个条件的 StrictAndMessageFilter 类。 | 不使用 filterData;而 filter1 和 filter2 具有相应消息筛选器的名称(也在表中),应使用 AND 将它们连接起来。 | <filter name="and1" filterType="And" filter1="address1" filter2="action1" /> |
自定义 | 一个用户定义的类型,此类型扩展 MessageFilter 类并具有采用字符串的构造函数。 | customType 特性是要创建的类的完全限定类型名称;filterData 是在创建筛选器时要传递给构造函数的字符串。 | <filter name="custom1" filterType="Custom" customType="CustomAssembly.CustomMsgFilter, CustomAssembly" filterData="Custom Data" /> |
EndpointName | 使用 EndpointNameMessageFilter 类根据消息已到达的服务终结点的名称来匹配消息。 | 服务终结点的名称,例如“serviceEndpoint1”。 该终结点应为在路由服务上公开的终结点之一。 | <filter name="stock1" filterType="Endpoint" filterData="SvcEndpoint" /> |
MatchAll | 使用 MatchAllMessageFilter 类。 该筛选器匹配所有到达的消息。 | 不使用 filterData。 该筛选器将始终匹配所有消息。 | <filter name="matchAll1" filterType="MatchAll" /> |
XPath | 使用 XPathMessageFilter 类匹配消息中的特定 XPath 查询。 | 在匹配消息时要使用的 XPath 查询。 | <filter name="XPath1" filterType="XPath" filterData="//ns:element" /> |
下面的示例定义使用 XPath、EndpointName 和 PrefixEndpointAddress 消息筛选器的筛选器条目。 该示例还演示如何对 RoundRobinFilter1 和 RoundRobinFilter2 条目使用自定义筛选器。
<filters>
<filter name="XPathFilter" filterType="XPath"
filterData="/s12:Envelope/s12:Header/custom:RoundingCalculator = 1"/>
<filter name="EndpointNameFilter" filterType="EndpointName"
filterData="calculatorEndpoint"/>
<filter name="PrefixAddressFilter" filterType="PrefixEndpointAddress"
filterData="http://localhost/routingservice/router/rounding/"/>
<filter name="RoundRobinFilter1" filterType="Custom"
customType="RoutingServiceFilters.RoundRobinMessageFilter,
RoutingService" filterData="group1"/>
<filter name="RoundRobinFilter2" filterType="Custom"
customType="RoutingServiceFilters.RoundRobinMessageFilter,
RoutingService" filterData="group1"/>
</filters>
注意
仅定义筛选器不会导致根据筛选器计算消息。 必须将筛选器添加到筛选器表中,然后会将该筛选器与路由服务公开的服务终结点相关联。
命名空间表
当使用 XPath 筛选器时,包含 XPath 查询的筛选器数据可能会因使用了命名空间而变得非常大。 为了解决此问题,路由服务提供使用命名空间表定义您自己的命名空间前缀的功能。
命名空间表是 NamespaceElement 对象的集合,用于定义可在 XPath 中使用的常用命名空间的命名空间前缀。 下面列出了命名空间表中包含的默认命名空间和命名空间前缀。
前缀 | 命名空间 |
---|---|
s11 | http://schemas.xmlsoap.org/soap/envelope |
s12 | http://www.w3.org/2003/05/soap-envelope |
wsaAugust2004 | http://schemas.xmlsoap.org/ws/2004/08/addressing |
wsa10 | http://www.w3.org/2005/08/addressing |
sm | http://schemas.microsoft.com/serviceModel/2004/05/xpathfunctions |
tempuri | http://tempuri.org |
ser | http://schemas.microsoft.com/2003/10/Serialization |
如果您确定您将在 XPath 查询中使用特定命名空间,则可以将此命名空间和唯一的命名空间前缀添加到命名空间表中,并在所有 XPath 查询中使用该前缀而不是使用完整命名空间。 下面的示例为命名空间 "http://my.custom.namespace"
定义了一个“custom”前缀,然后在 filterData 包含的 XPath 查询中使用该前缀。
<namespaceTable>
<add prefix="custom" namespace="http://my.custom.namespace/"/>
</namespaceTable>
<filters>
<filter name="XPathFilter" filterType="XPath" filterData="/s12:Envelope/s12:Header/custom:RoundingCalculator = 1"/>
</filters>
筛选器表
各筛选器元素定义可应用于消息的逻辑比较,而筛选器表提供筛选器元素和目标客户端终结点之间的关联。 筛选器表是 FilterTableEntryElement 对象的命名集合,用于定义筛选器、主目标终结点和替代备份终结点列表之间的关联。 筛选器表条目还允许您为每个筛选条件指定可选优先级。 下面的示例定义两个筛选器,然后定义一个用于将每个筛选器与目标终结点相关联的筛选器表。
<routing>
<filters>
<filter name="AddAction" filterType="Action" filterData="Add" />
<filter name="SubtractAction" filterType="Action" filterData="Subtract" />
</filters>
<filterTables>
<table name="routingTable1">
<filters>
<add filterName="AddAction" endpointName="Addition" />
<add filterName="SubtractAction" endpointName="Subtraction" />
</filters>
</table>
</filterTables>
</routing>
筛选器计算优先级
默认情况下,将同时计算筛选器表中的所有条目,计算的消息将路由至与每个匹配的筛选器条目相关联的终结点。 如果多个筛选器的计算结果均为 true
,并且消息是单向或双工消息,则会将此消息多播到所有匹配筛选器所对应的终结点。 由于只能向客户端返回一个答复,因此无法多播请求-答复消息。
通过为各筛选器指定优先级别,可以实现更复杂的路由逻辑;路由服务将首先计算处于最高优先级别的所有筛选器。 如果某一消息与此级别的筛选器相匹配,则不会处理较低优先级的筛选器。 例如,将首先根据优先级为 2 的所有筛选器计算传入的单向消息。 该消息不与此优先级别的任何筛选器匹配,因此,接着将根据优先级为 1 的筛选器比较此消息。 有两个优先级为 1 的筛选器与此消息匹配,并且由于该消息是单向消息,因此将被路由至两个目标终结点。 由于已在优先级为 1 的筛选器中找到了匹配项,因此不会计算优先级为 0 的筛选器。
备注
如果未指定优先级,则使用默认优先级 0。
下面的示例定义一个筛选器表,该筛选器表为其引用的筛选器指定优先级 2、1 和 0。
<filterTables>
<filterTable name="filterTable1">
<add filterName="XPathFilter" endpointName="roundingCalcEndpoint"
priority="2"/>
<add filterName="EndpointNameFilter" endpointName="regularCalcEndpoint"
priority="1"/>
<add filterName="PrefixAddressFilter" endpointName="roundingCalcEndpoint"
priority="1"/>
<add filterName="MatchAllMessageFilter" endpointName="defaultCalcEndpoint"
priority="0"/>
</filterTable>
</filterTables>
在上面的示例中,如果消息与 XPathFilter 匹配,则将该消息路由至 roundingCalcEndpoint,并且不会计算表中的任何其他筛选器,这是因为所有其他筛选器都具有较低的优先级。 但是,如果此消息与 XPathFilter 不匹配,则会根据下一较低优先级的所有筛选器(即 EndpointNameFilter 和 PrefixAddressFilter)计算此消息。
备注
如有可能,请使用独占筛选器,而不是指定优先级,因为优先级计算可能会导致性能降级。
备份列表
筛选器表中的各个筛选器可以选择指定一个备份列表,即终结点的命名集合 (BackupEndpointCollection)。 该集合包含在向主终结点(在 CommunicationException 中指定)发送消息期间发生 EndpointName 时,消息将传输到的终结点的有序列表。 下面的示例定义一个名为“backupServiceEndpoints”的备份列表,该列表包含两个终结点。
<filterTables>
<filterTable name="filterTable1">
<add filterName="MatchAllFilter1" endpointName="Destination" backupList="backupEndpointList"/>
</filterTable>
</filterTables>
<backupLists>
<backupList name="backupEndpointList">
<add endpointName="backupServiceQueue" />
<add endpointName="alternateServiceQueue" />
</backupList>
</backupLists>
在上面的示例中,如果发送到主终结点“Destination”失败,路由服务将尝试按终结点的列出顺序发送到各个终结点,即首先发送到 backupServiceQueue,如果发送到 backupServiceQueue 失败,则随后发送到 alternateServiceQueue。 如果所有备份终结点均失败,则会返回错误。