Como: usar filtros
Este tópico descreve as etapas básicas necessárias para criar uma configuração de roteamento que usa vários filtros. Neste exemplo, as mensagens são roteadas para duas implementações de um serviço de calculadora, regularCalc e roundingCalc. Ambas as implementações dão suporte às mesmas operações. No entanto, um serviço arredonda todos os cálculos para o valor inteiro mais próximo antes de retornar. Um aplicativo cliente deve ser capaz de indicar se deve usar a versão de arredondamento do serviço. Se nenhuma preferência de serviço for expressa, a mensagem será balanceada entre os dois serviços. As operações expostas por ambos os serviços são:
Adicionar
Subtrair
Multiplicar
Dividir
Como ambos os serviços implementam as mesmas operações, você não pode usar o filtro Ação, pois a ação especificada na mensagem não será exclusiva. Em vez disso, você deve fazer um trabalho adicional para garantir que as mensagens sejam roteadas para os pontos de extremidade apropriados.
Determinar dados exclusivos
Como ambas as implementações de serviço lidam com as mesmas operações e são essencialmente idênticas além dos dados retornados, os dados base contidos em mensagens enviadas de aplicativos cliente não são exclusivos o suficiente para permitir que você determine como rotear a solicitação. No entanto, se o aplicativo cliente adicionar um valor de cabeçalho exclusivo à mensagem, você poderá usar esse valor para determinar como a mensagem deve ser roteada.
Para este exemplo, se o aplicativo cliente precisar que a mensagem seja processada pela calculadora de arredondamento, ele adicionará um cabeçalho personalizado usando o seguinte código:
messageHeadersElement.Add(MessageHeader.CreateHeader("RoundingCalculator", "http://my.custom.namespace/", "rounding"));
Agora você pode usar o filtro XPath para inspecionar mensagens desse cabeçalho e rotear mensagens que contêm o cabeçalho para o serviço roundCalc.
Além disso, o Serviço de Roteamento expõe dois pontos de extremidade de serviço virtual que podem ser usados com os filtros EndpointName, EndpointAddress ou PrefixEndpointAddress para rotear exclusivamente as mensagens de entrada para uma implementação de calculadora específica com base no ponto de extremidade para o qual o aplicativo cliente envia a solicitação.
Definir pontos de extremidade
Ao definir os pontos de extremidade usados pelo serviço de roteamento, primeiro você deve determinar a forma do canal usado por seus clientes e serviços. Nesse cenário, ambos os serviços de destino usam um padrão de solicitação-resposta, portanto, o IRequestReplyRouter é utilizado. O exemplo a seguir define os pontos de extremidade de serviço expostos pelo serviço de roteamento.
<services> <service behaviorConfiguration="routingConfiguration" name="System.ServiceModel.Routing.RoutingService"> <host> <baseAddresses> <add baseAddress="http://localhost/routingservice/router" /> </baseAddresses> </host> <!--Set up the inbound endpoints for the Routing Service--> <!--first create the general router endpoint--> <endpoint address="general" binding="wsHttpBinding" name="routerEndpoint" contract="System.ServiceModel.Routing.IRequestReplyRouter" /> <!--create a virtual endpoint for the regular calculator service--> <endpoint address="regular/calculator" binding="wsHttpBinding" name="calculatorEndpoint" contract="System.ServiceModel.Routing.IRequestReplyRouter" /> <!--now create a virtual endpoint for the rounding calculator--> <endpoint address="rounding/calculator" binding="wsHttpBinding" name="roundingEndpoint" contract="System.ServiceModel.Routing.IRequestReplyRouter" /> </service> </services>
Com essa configuração, o serviço de roteamento expõe três pontos de extremidade separados. Dependendo das opções de tempo de execução, o aplicativo cliente envia mensagens para um desses endereços. As mensagens que chegam a um dos pontos de extremidade de serviço "virtuais" ("rounding/calculator" ou "regular/calculator") são encaminhadas para a implementação da calculadora correspondente. Se o aplicativo cliente não enviar a solicitação para um determinado ponto de extremidade, a mensagem será endereçada ao ponto de extremidade geral. Independentemente do ponto de extremidade escolhido, o aplicativo cliente também pode optar por incluir o cabeçalho personalizado para indicar que a mensagem deve ser encaminhada para a implementação da calculadora de arredondamento.
O exemplo a seguir define os pontos de extremidade do cliente (destino) para os quais o serviço de roteamento roteia mensagens.
<client> <endpoint name="regularCalcEndpoint" address="net.tcp://localhost:9090/servicemodelsamples/service/" binding="netTcpBinding" contract="*" /> <endpoint name="roundingCalcEndpoint" address="net.tcp://localhost:8080/servicemodelsamples/service/" binding="netTcpBinding" contract="*" /> </client>
Esses pontos de extremidade são usados na tabela de filtro para indicar o ponto de extremidade de destino ao qual a mensagem é enviada quando corresponde a um filtro específico.
Definir Filtros
Para rotear mensagens com base no cabeçalho personalizado "RoundingCalculator" que o aplicativo cliente adiciona à mensagem, defina um filtro que use uma consulta XPath para verificar a presença desse cabeçalho. Como esse cabeçalho é definido usando um namespace personalizado, adicione também uma entrada de namespace que defina um prefixo de namespace personalizado de "custom" que é usado na consulta XPath. O exemplo a seguir define a seção de roteamento, a tabela de namespace e o filtro XPath necessários.
<routing> <!-- use the namespace table element to define a prefix for our custom namespace--> <namespaceTable> <add prefix="custom" namespace="http://my.custom.namespace/"/> </namespaceTable> <filters> <!--define the different message filters--> <!--define an xpath message filter to look for the custom header coming from the client--> <filter name="XPathFilter" filterType="XPath" filterData="/s12:Envelope/s12:Header/custom:RoundingCalculator = 'rounding'"/> </filters> </routing>
Esse MessageFilter procura um cabeçalho RoundingCalculator na mensagem que contém um valor de "rounding". Esse cabeçalho é definido pelo cliente para indicar que a mensagem deve ser roteada para o serviço roundingCalc.
Observação
O prefixo de namespace s12 é definido por padrão na tabela de namespace e representa o namespace
http://www.w3.org/2003/05/soap-envelope
.Você também deve definir filtros que procuram mensagens recebidas nos dois pontos de extremidade virtuais. O primeiro ponto de extremidade virtual é o ponto de extremidade "regular/calculator". O cliente pode enviar solicitações para esse ponto de extremidade para indicar que a mensagem deve ser roteada para o serviço regularCalc. A configuração a seguir define um filtro que usa o EndpointNameMessageFilter para determinar se a mensagem chegou por meio de um ponto de extremidade com o nome especificado em filterData.
<!--define an endpoint name filter looking for messages that show up on the virtual regular calculator endpoint--> <filter name="EndpointNameFilter" filterType="EndpointName" filterData="calculatorEndpoint"/>
Se uma mensagem for recebida pelo ponto de extremidade de serviço chamado "calculatorEndpoint", esse filtro será avaliado como
true
.Em seguida, defina um filtro que procure mensagens enviadas para o endereço do roundingEndpoint. O cliente pode enviar solicitações para esse ponto de extremidade para indicar que a mensagem deve ser roteada para o serviço roundingCalc. A configuração a seguir define um filtro que usa o PrefixEndpointAddressMessageFilter para determinar se a mensagem chegou ao ponto de extremidade "rounding/calculator".
<!--define a filter looking for messages that show up with the address prefix. The corresponds to the rounding calc virtual endpoint--> <filter name="PrefixAddressFilter" filterType="PrefixEndpointAddress" filterData="http://localhost/routingservice/router/rounding/"/>
Se uma mensagem for recebida em um endereço que começa com
http://localhost/routingservice/router/rounding/
, esse filtro será avaliado como true. Como o endereço base usado por essa configuração éhttp://localhost/routingservice/router
e o endereço especificado para o roundingEndpoint é "rounding/calculator", o endereço completo usado para se comunicar com esse ponto de extremidade éhttp://localhost/routingservice/router/rounding/calculator
, que corresponde a esse filtro.Observação
O filtro PrefixEndpointAddress não avalia o nome do host ao executar uma correspondência, pois um único host pode ser referenciado usando uma variedade de nomes de host que podem ser maneiras válidas de fazer referência ao host do aplicativo cliente. Por exemplo, todos os seguintes podem se referir ao mesmo host:
- localhost
- 127.0.0.1
www.contoso.com
- ContosoWeb01
O filtro final deve dar suporte ao roteamento de mensagens que chegam ao ponto de extremidade geral sem o cabeçalho personalizado. Para esse cenário, as mensagens devem alternar entre os serviços regularCalc e roundingCalc. Para dar suporte ao roteamento "round robin" dessas mensagens, use um filtro personalizado que permite que uma instância de filtro corresponda a cada mensagem processada. O seguinte define duas instâncias de um RoundRobinMessageFilter, que são agrupadas para indicar que elas devem alternar entre si.
<!-- Set up the custom message filters. In this example, we'll use the example round robin message filter, which alternates between the references--> <filter name="RoundRobinFilter1" filterType="Custom" customType="CustomFilterAssembly.RoundRobinMessageFilter, CustomFilterAssembly" filterData="group1"/> <filter name="RoundRobinFilter2" filterType="Custom" customType="CustomFilterAssembly.RoundRobinMessageFilter, CustomFilterAssembly" filterData="group1"/>
Durante o tempo de execução, esse tipo de filtro alterna entre todas as instâncias de filtro definidas desse tipo que são configuradas como o mesmo grupo em uma coleção. Isso faz com que as mensagens processadas por esse filtro personalizado se alternem entre retornar
true
paraRoundRobinFilter1
eRoundRobinFilter2
.
Definir tabelas de filtro
Para associar os filtros a pontos de extremidade de cliente específicos, você deve colocá-los dentro de uma tabela de filtro. Este cenário de exemplo também usa configurações de prioridade de filtro, que é uma configuração opcional que permite que você indique a ordem na qual os filtros são processados. Se nenhuma prioridade de filtro for especificada, todos os filtros serão avaliados simultaneamente.
Observação
Embora a especificação de uma prioridade de filtro permita controlar a ordem na qual os filtros são processados, isso pode afetar negativamente o desempenho do serviço de roteamento. Quando possível, construa a lógica de filtro para que o uso de prioridades de filtro não seja necessário.
O seguinte define a tabela de filtro e adiciona o "XPathFilter" definido anteriormente à tabela com uma prioridade de 2. Essa entrada também especifica que, se
XPathFilter
corresponder à mensagem, a mensagem será roteada pararoundingCalcEndpoint
.<routing> ... <filters> ... </filters> <filterTables> <table name="filterTable1"> <entries> <!--add the filters to the message filter table--> <!--first look for the custom header, and if we find it, send the message to the rounding calc endpoint--> <add filterName="XPathFilter" endpointName="roundingCalcEndpoint" priority="2"/> </entries> </table> </filterTables> </routing>
Ao especificar uma prioridade de filtro, os filtros de prioridade mais alta são avaliados primeiro. Se um ou mais filtros em um nível de prioridade específico corresponderem, nenhum filtro em níveis de prioridade mais baixos será avaliado. Para esse cenário, 2 é a prioridade mais alta especificada e essa é a única entrada de filtro nesse nível.
As entradas de filtro foram definidas para verificar se uma mensagem é recebida em um ponto de extremidade específico inspecionando o nome do ponto de extremidade ou o prefixo de endereço. As entradas a seguir adicionam ambas as entradas de filtro à tabela de filtro e as associam aos pontos de extremidade de destino para os quais a mensagem será roteada. Esses filtros são definidos como uma prioridade de 1 para indicar que eles só devem ser executados se o filtro XPath anterior não corresponder à mensagem.
<!--if the header wasn't there, send the message based on which virtual endpoint it arrived at--> <!--we determine this through the endpoint name, or through the address prefix--> <add filterName="EndpointNameFilter" endpointName="regularCalcEndpoint" priority="1"/> <add filterName="PrefixAddressFilter" endpointName="roundingCalcEndpoint" priority="1"/>
Como esses filtros têm uma prioridade de filtro de 1, eles só serão avaliados se o filtro no nível de prioridade 2 não corresponder à mensagem. Além disso, como ambos os filtros têm o mesmo nível de prioridade, eles serão avaliados simultaneamente. Como ambos os filtros são mutuamente exclusivos, é possível que apenas um ou outro corresponda a uma mensagem.
Se uma mensagem não corresponder a nenhum dos filtros anteriores, a mensagem será recebida por meio do ponto de extremidade de serviço genérico e não conterá nenhuma informação de cabeçalho que indique para onde roteá-la. Essas mensagens devem ser manipuladas pelo filtro personalizado, que as balanceia entre os dois serviços de calculadora. O exemplo a seguir demonstra como adicionar as entradas de filtro à tabela de filtro, sendo que cada filtro está associado a um dos dois pontos de extremidade de destino.
<!--if none of the other filters have matched, this message showed up on the default router endpoint, with no custom header--> <!--round robin these requests between the two services--> <add filterName="RoundRobinFilter1" endpointName="regularCalcEndpoint" priority="0"/> <add filterName="RoundRobinFilter2" endpointName="roundingCalcEndpoint" priority="0"/>
Como essas entradas especificam uma prioridade de 0, elas só serão avaliadas se nenhum filtro de uma prioridade mais alta corresponder à mensagem. Além disso, como ambos são da mesma prioridade, eles são avaliados simultaneamente.
Conforme mencionado anteriormente, o filtro personalizado usado por essas definições de filtro avalia apenas uma ou outra como
true
para cada mensagem recebida. Como apenas dois filtros são definidos usando esse filtro, com a mesma configuração de grupo especificada, o efeito é que o serviço de roteamento alterna entre o envio para o RegularCalcEndpoint e o RoundingCalcEndpoint.Para avaliar as mensagens em relação aos filtros, a tabela de filtros deve primeiro ser associada aos pontos de extremidade de serviço que serão usados para receber mensagens. O exemplo a seguir demonstra como associar a tabela de roteamento aos pontos de extremidade de serviço usando o comportamento de roteamento:
<behaviors> <!--default routing service behavior definition--> <serviceBehaviors> <behavior name="routingConfiguration"> <routing filterTableName="filterTable1" /> </behavior> </serviceBehaviors> </behaviors>
Exemplo
Veja a seguir a listagem completa do arquivo de configuração.
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright (c) Microsoft Corporation. All rights reserved -->
<configuration>
<system.serviceModel>
<services>
<service behaviorConfiguration="routingConfiguration"
name="System.ServiceModel.Routing.RoutingService">
<host>
<baseAddresses>
<add baseAddress="http://localhost/routingservice/router" />
</baseAddresses>
</host>
<!--Set up the inbound endpoints for the Routing Service-->
<!--first create the general router endpoint-->
<endpoint address="general"
binding="wsHttpBinding"
name="routerEndpoint"
contract="System.ServiceModel.Routing.IRequestReplyRouter" />
<!--create a virtual endpoint for the regular calculator service-->
<endpoint address="regular/calculator"
binding="wsHttpBinding"
name="calculatorEndpoint"
contract="System.ServiceModel.Routing.IRequestReplyRouter" />
<!--now create a virtual endpoint for the rounding calculator-->
<endpoint address="rounding/calculator"
binding="wsHttpBinding"
name="roundingEndpoint"
contract="System.ServiceModel.Routing.IRequestReplyRouter" />
</service>
</services>
<behaviors>
<!--default routing service behavior definition-->
<serviceBehaviors>
<behavior name="routingConfiguration">
<routing filterTableName="filterTable1" />
</behavior>
</serviceBehaviors>
</behaviors>
<client>
<!--set up the destination endpoints-->
<endpoint name="regularCalcEndpoint"
address="net.tcp://localhost:9090/servicemodelsamples/service/"
binding="netTcpBinding"
contract="*" />
<endpoint name="roundingCalcEndpoint"
address="net.tcp://localhost:8080/servicemodelsamples/service/"
binding="netTcpBinding"
contract="*" />
</client>
<routing>
<!-- use the namespace table element to define a prefix for our custom namespace-->
<namespaceTable>
<add prefix="custom" namespace="http://my.custom.namespace/"/>
</namespaceTable>
<filters>
<!--define the different message filters-->
<!--define an xpath message filter to look for the custom header coming from the client-->
<filter name="XPathFilter" filterType="XPath" filterData="/s12:Envelope/s12:Header/custom:RoundingCalculator = 'rounding'"/>
<!--define an endpoint name filter looking for messages that show up on the virtual regular calculator endpoint-->
<filter name="EndpointNameFilter" filterType="EndpointName" filterData="calculatorEndpoint"/>
<!--define a filter looking for messages that show up with the address prefix. The corresponds to the rounding calc virtual endpoint-->
<filter name="PrefixAddressFilter" filterType="PrefixEndpointAddress" filterData="http://localhost/routingservice/router/rounding/"/>
<!--Set up the custom message filters. In this example, we'll use the example round robin message filter, which alternates between the references-->
<filter name="RoundRobinFilter1" filterType="Custom" customType="CustomFilterAssembly.RoundRobinMessageFilter, CustomFilterAssembly" filterData="group1"/>
<filter name="RoundRobinFilter2" filterType="Custom" customType="CustomFilterAssembly.RoundRobinMessageFilter, CustomFilterAssembly" filterData="group1"/>
</filters>
<filterTables>
<table name="filterTable1">
<entries>
<!--add the filters to the message filter table-->
<!--first look for the custom header, and if we find it, send the message to the rounding calc endpoint-->
<add filterName="XPathFilter" endpointName="roundingCalcEndpoint" priority="2"/>
<!--if the header wasn't there, send the message based on which virtual endpoint it arrived at-->
<!--we determine this through the endpoint name, or through the address prefix-->
<add filterName="EndpointNameFilter" endpointName="regularCalcEndpoint" priority="1"/>
<add filterName="PrefixAddressFilter" endpointName="roundingCalcEndpoint" priority="1"/>
<!--if none of the other filters have matched, this message showed up on the default router endpoint, with no custom header-->
<!--round robin these requests between the two services-->
<add filterName="RoundRobinFilter1" endpointName="regularCalcEndpoint" priority="0"/>
<add filterName="RoundRobinFilter2" endpointName="roundingCalcEndpoint" priority="0"/>
</entries>
</table>
</filterTables>
</routing>
</system.serviceModel>
</configuration>