Cómo: Utilizar los filtros
Este tema describe los pasos básicos necesarios para crear una configuración de enrutamiento que utiliza múltiples filtros. En este ejemplo, los mensajes se enrutan a dos implementaciones de un servicio de la calculadora, regularCalc y roundingCalc. Ambas implementaciones admiten las mismas operaciones; sin embargo, un servicio redondea todos los cálculos al valor entero más cercano antes de devolverlos. Una aplicación cliente debe poder indicar si se debe utilizar la versión del redondeo del servicio; si no se especifica ninguna preferencia de servicio, la carga del mensaje se equilibra entre los dos servicios. Las operaciones expuestas por ambos servicios son:
Sumar
Restar
Multiplicar
Dividir
Dado que ambos servicios implementan las mismas operaciones, no puede utilizar el filtro Action porque la acción especificada en el mensaje no será única. En su lugar, debe realizar un trabajo adicional para asegurarse de que los mensajes se enrutan a los extremos adecuados.
Determinación de datos únicos
Como ambas implementaciones del servicio administran las mismas operaciones y son prácticamente idénticas exceptuando los datos que devuelven, los datos base incluidos en mensajes enviados de las aplicaciones cliente no son lo suficientemente exclusivos como para permitirle determinar cómo enrutar la solicitud. Sin embargo, si la aplicación cliente agrega un valor de encabezado único al mensaje, puede utilizar este valor para determinar cómo se debería enrutar el mensaje.
En este ejemplo, si la aplicación cliente necesita procesar el mensaje mediante la calculadora de redondeo, agrega un encabezado personalizado mediante el siguiente código:
messageHeadersElement.Add(MessageHeader.CreateHeader("RoundingCalculator", "http://my.custom.namespace/", "rounding"));
Ahora, puede utilizar el filtro XPath para inspeccionar los mensajes de este encabezado y enrutar mensajes que contengan el encabezado al servicio roundCalc.
Además el servicio de enrutamiento expone dos extremos de servicio virtuales que se pueden utilizar con los filtros EndpointName, EndpointAddress o PrefixEndpointAddress para enrutar de forma exclusiva los mensajes entrantes a una implementación de la calculadora concreta en función del extremo al que la aplicación cliente envía la solicitud.
Definición de los extremos
Al definir los extremos utilizados por el servicio de enrutamiento, debería determinar primero la forma del canal empleado por sus clientes y servicios. En este escenario, ambos servicios de destino utilizan un patrón de solicitud-respuesta, de modo que se utiliza IRequestReplyRouter. En el siguiente ejemplo, se definen los extremos de servicio expuestos por el servicio de enrutamiento.
<services> <service behaviorConfiguration="routingConfiguration" name="System.ServiceModel.Routing.RoutingService"> <host> <baseAddresses> <add baseAddress="https://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>
Con esta configuración, el servicio de enrutamiento expone tres extremos independientes. Según las opciones de tiempo de ejecución, la aplicación cliente envía mensajes a una de estas direcciones. Los mensajes que llegan a uno de los extremos de servicio "virtuales" ("redondeo/calculadora" o "normal/calculadora") se reenvían a la implementación de la calculadora correspondiente. Si la aplicación cliente no envía la solicitud a un extremo determinado, el mensaje se dirige al extremo general. Independientemente del extremo elegido, la aplicación cliente también puede decidir incluir el encabezado personalizado para indicar que el mensaje se debería reenviar a la implementación de calculadora de redondeo.
El siguiente ejemplo define los extremos del cliente (destino) a los que el servicio de enrutamiento enruta los mensajes.
<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>
Estos extremos se utilizan en la tabla de filtros para indicar el extremo de destino al que se envía el mensaje cuando coincide con un filtro concreto.
Definición de filtros
Para enrutar mensajes en función del encabezado personalizado "RoundingCalculator" que la aplicación cliente agrega al mensaje, defina un filtro que utilice una consulta XPath para comprobar la presencia de este encabezado. Dado que este encabezado se define utilizando un espacio de nombres personalizado, agregue también una entrada de espacio de nombres que defina un prefijo de espacio de nombres "personalizado" que se utilizará en la consulta XPath. En el siguiente ejemplo, se define la sección de enrutamiento necesaria, la tabla de espacio de nombres y el filtro XPath.
<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>
MessageFilter busca un encabezado RoundingCalculator en el mensaje que contenga un valor de "redondeo". El cliente establece este encabezado para indicar que el mensaje se debería enrutar al servicio de roundingCalc.
Nota: El prefijo de espacio de nombres s12 se define de forma predeterminada en la tabla de espacio de nombres y representa el espacio de nombres "http://www.w3.org/2003/05/soap-envelope". También debe definir los filtros que buscan mensajes recibidos en los dos extremos virtuales. El primer extremo virtual es el extremo "normal/calculadora". El cliente puede enviar solicitudes a este extremo para indicar que el mensaje se debería enrutar al servicio de regularCalc. La siguiente configuración define un filtro que utiliza EndpointNameMessageFilter para determinar si el mensaje llegó a través de un extremo con el nombre especificado en 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"/>
Si el extremo de servicio recibe un mensaje denominado "calculatorEndpoint", este filtro se evalúa como true.
A continuación, defina un filtro que busque los mensajes enviados a la dirección de roundingEndpoint. El cliente puede enviar solicitudes a este extremo para indicar que el mensaje se debería enrutar al servicio de roundingCalc. La siguiente configuración define un filtro que utiliza PrefixEndpointAddressMessageFilter para determinar si el mensaje llegó al extremo de "redondeo/calculadora".
<!--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="https://localhost/routingservice/router/rounding/"/>
Si se recibe un mensaje en una dirección que comienza por "https://localhost/routingservice/router/rounding/", este filtro se evalúa como true. Dado que la dirección base utilizada por esta configuración es "https://localhost/routingservice/router" y la dirección especificada para roundingEndpoint es "redondeo/calculadora", la dirección completa empleada para comunicarse con este extremo es "https://localhost/routingservice/router/rounding/calculator", que coincide con este filtro.
Nota: El filtro PrefixEndpointAddress no evalúa el nombre de host al realizar una coincidencia, porque se puede hacer referencia a un host único utilizando diversos nombres de host que pueden ser todos ellos métodos válidos para hacer referencia al host de la aplicación cliente. Por ejemplo, todos los nombres siguientes pueden hacer referencia al mismo host: - localhost
- 127.0.0.1
- www.contoso.com
- ContosoWeb01
- localhost
El filtro final debe admitir el enrutamiento de mensajes que llegan al extremo general sin el encabezado personalizado. En este escenario, los mensajes deberían alternar entre los servicios de regularCalc y roundingCalc. Para admitir el enrutamiento de "operación por turnos" de estos mensajes, utilice un filtro personalizado que permita que una instancia del filtro busque coincidencias con cada mensaje procesado. A continuación, se definen dos instancias de RoundRobinMessageFilter, que se agrupan para indicar que deberían alternar entre sí.
<!-- 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 el tiempo de ejecución, este tipo de filtro alterna entre todas las instancias de filtro definidas de este tipo que están configuradas como el mismo grupo en una recopilación. Esto hace que los mensajes procesados mediante este filtro personalizado se alternen para devolver true a RoundRobinFilter1 y RoundRobinFilter2.
Definición de tablas de filtros
Para asociar los filtros a extremos de cliente concretos, debe colocarlos dentro de una tabla de filtros. Este ejemplo de escenario también utiliza una configuración de prioridad de filtros, que es una configuración opcional que le permite indicar el orden en el que se procesan los filtros. Si no se especifica ninguna prioridad de filtro, todos los filtros se evalúan simultáneamente.
Nota: Aunque una prioridad de filtro le permite controlar el orden en el que se procesan los filtros, puede afectar negativamente al rendimiento del servicio de enrutamiento. Cuando sea posible, cree una lógica de filtro para que no se requiera el uso de prioridades de filtro. A continuación, se define la tabla de filtros y se agrega el "XPathFilter" definido anteriormente a la tabla con la prioridad 2. Esta entrada también especifica que, si "XPathFilter" coincide con el mensaje, el mensaje se enrutará a "roundingCalcEndpoint".
<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>
Al especificar una prioridad de filtro, los filtros con prioridad máxima se evalúan primero. Si coinciden uno o más filtros en un nivel de prioridad concreto, no se evaluará ningún filtro en los niveles de menor prioridad. Para este escenario, 2 es la prioridad máxima especificada y ésta es la única entrada de filtro en este nivel.
Las entradas de filtro se han definido para comprobar si se recibe un mensaje se recibe en un extremo concreto inspeccionando el nombre de extremo o el prefijo de dirección. Las siguientes entradas suman ambas entradas de filtro a la tabla de filtros y las asocian a los extremos de destino a los que se enrutará el mensaje. Estos filtros están establecidos con una prioridad 1 para indicar que solo se deberían ejecutar si el filtro XPath anterior no coincidiera con el mensaje.
<!--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"/>
Dado que estos filtros tienen una prioridad de filtro de 1, solo se evaluarán si el filtro de nivel de prioridad 2 no coincide con el mensaje. Asimismo, como ambos filtros tienen el mismo nivel de prioridad, se evaluarán simultáneamente. Dado que ambos filtros son mutuamente excluyentes, es posible que solo uno u otro coincida con un mensaje.
Si un mensaje no coincide con ninguno de los filtros anteriores, dicho mensaje se recibió a través del extremo de servicio genérico y no contenía ninguna información de encabezado que indicara dónde enrutarlo. El filtro personalizado administrará estos mensajes para equilibrar su carga entre los dos servicios de la calculadora. En el siguiente ejemplo, se muestra cómo agregar las entradas de filtro a la tabla de filtros; cada filtro se asocia a uno de los dos extremos 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"/>
Dado que estas entradas especifican una prioridad de 0, solo se evaluarán si ningún filtro con una prioridad más alta coincide con el mensaje. Asimismo, como ambos tienen la misma prioridad, se evaluarán simultáneamente.
Tal y como se ha indicado previamente, el filtro personalizado utilizado por estas definiciones de filtro solo evalúa uno u otro como true por cada mensaje recibido. Dado que solo se han definido dos filtros utilizando este filtro, con la misma configuración de grupo especificada, el efecto es que el servicio de enrutamiento alterna entre el envío a regularCalcEndpoint y a RoundingCalcEndpoint.
Para evaluar los mensajes con respecto a los filtros, la tabla de filtros debe estar asociada primero a los extremos de servicio que se utilizarán para recibir mensajes. En el siguiente ejemplo, se muestra cómo asociar la tabla de enrutamiento a los extremos de servicio utilizando el comportamiento de enrutamiento:
<behaviors> <!--default routing service behavior definition--> <serviceBehaviors> <behavior name="routingConfiguration"> <routing filterTableName="filterTable1" /> </behavior> </serviceBehaviors> </behaviors>
Ejemplo
A continuación, se muestra una lista completa del archivo de configuración.
<?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="https://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="https://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>