How To: Service Data Partitioning
This topic outlines the basic steps required to partition messages across multiple instances of the same destination service. Service data partitioning is typically used when you need to scale a service in order to provide better quality of service, or when you need to handle requests from different customers in a specific way. For example, messages from high value or “Gold” customers may need to be processed at a higher priority than messages from a standard customer.
In this example, messages are routed to one of two instances of the regularCalc service. Both instances of the service are identical; however the service represented by the calculator1 endpoint processes messages received from high value customers, the calculator 2 endpoint processes messages from other customers
The message sent from the client does not have any unique data that can be used to identify which service instance the message should be routed to. To allow each client to route data to a specific destination service we will implement two service endpoints that will be used to receive messages.
Note
While this example uses specific endpoints to partition data, this could also be accomplished using information contained within the message itself such as header or body data.
Implement Service Data Partitioning
Create the basic Routing Service configuration by specifying the service endpoints exposed by the service. The following example defines two endpoints, which will be used to receive messages. It also defines the client endpoints, which are used to send messages to the regularCalc service instances.
<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--> <!--create the endpoints for the calculator service--> <endpoint address="calculator1" binding="wsHttpBinding" name="calculator1Endpoint" contract="System.ServiceModel.Routing.IRequestReplyRouter" /> <endpoint address="calculator2" binding="wsHttpBinding" name="calculator2Endpoint" contract="System.ServiceModel.Routing.IRequestReplyRouter" /> </service> </services> <client> <!--set up the destination endpoints--> <endpoint name="CalcEndpoint1" address="net.tcp://localhost:9090/servicemodelsamples/service/" binding="netTcpBinding" contract="*" /> <endpoint name="CalcEndpoint2" address="net.tcp://localhost:8080/servicemodelsamples/service/" binding="netTcpBinding" contract="*" /> </client>
Define the filters used to route messages to the destination endpoints. For this example, the EndpointName filter is used to determine which service endpoint received the message. The following example defines the necessary routing section and filters.
<filters> <!--define the different message filters--> <!--define endpoint name filters looking for messages that show up on the virtual endpoints--> <filter name="HighPriority" filterType="EndpointName" filterData="calculator1Endpoint"/> <filter name="NormalPriority" filterType="EndpointName" filterData="calculator2Endpoint"/> </filters>
Define the filter table, which associates each filter with a client endpoint. In this example, the message will be routed based on the specific endpoint it was received over. Since the message can only match one of the two possible filters, there is no need for using filter priority to control to the order in which filters are evaluated.
The following defines the filter table and adds the filters defined earlier.
<filterTables> <filterTable name="filterTable1"> <!--add the filters to the message filter table--> <add filterName="HighPriority" endpointName="CalcEndpoint1"/> <add filterName="NormalPriority" endpointName="CalcEndpoint2"/> </filterTable> </filterTables>
To evaluate incoming messages against the filters contained in the table, you must associate the filter table with the service endpoints by using the routing behavior. The following example demonstrates associating “filterTable1” with the service endpoints:
<behaviors> <!--default routing service behavior definition--> <serviceBehaviors> <behavior name="routingConfiguration"> <routing filterTableName="filterTable1" /> </behavior> </serviceBehaviors> </behaviors>
Example
The following is a complete listing of the configuration file.
<?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-->
<!--create the endpoints for the calculator service-->
<endpoint address="calculator1"
binding="wsHttpBinding"
name="calculator1Endpoint"
contract="System.ServiceModel.Routing.IRequestReplyRouter" />
<endpoint address="calculator2"
binding="wsHttpBinding"
name="calculator2Endpoint"
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="CalcEndpoint1"
address="net.tcp://localhost:9090/servicemodelsamples/service/"
binding="netTcpBinding"
contract="*" />
<endpoint name="CalcEndpoint2"
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-->
<filters>
<!--define the different message filters-->
<!--define endpoint name filters looking for messages that show up on the virtual endpoints-->
<filter name="HighPriority" filterType="EndpointName"
filterData="calculator1Endpoint"/>
<filter name="NormalPriority" filterType="EndpointName"
filterData="calculator2Endpoint"/>
</filters>
<filterTables>
<filterTable name="filterTable1">
<!--add the filters to the message filter table-->
<add filterName="HighPriority" endpointName="CalcEndpoint1"/>
<add filterName="NormalPriority" endpointName="CalcEndpoint2"/>
</filterTable>
</filterTables>
</routing>
</system.serviceModel>
</configuration>