天天看点

WCF 4 Step By Step Chapter14 Note 2 (Routing Messages)

Implementing Routing

Sometimes it is useful to be able to forward messages sent to a service to an entirely different service for handling.

-The front-end service can implement a routing mechanism,forwarding requests on the real services by examining the action or address in each message. This technique is known as address-based routing.

-An alternative scheme is to route messages based on their contents rather than on the action being requested; this mechanism is known as content-based routing.

WCF provides two primary mechanisms that you can employ toimplement routing, depending upon the complexity of your requirements.

            -The RoutingService class in the System.Service Model.Routing namespace enables you to provide configuration information to route messages to other services

            -if youneed to implement a more dynamic or low-level approach such as that required by a load-balancing router, you can route messages manually 

Routing Messages Manually

A service can expose multiple endpoints, each associated with the same or a different contract.When a WCF service receives a message, it must examine the message to determine which service endpoint should actually process it. You cancustomize the way in which WCF selects the endpoint to use, and this provides a mechanismfor you to change the way in which WCF routes messages within a service.

ChannelDispatcher and EndpointDispatcher Objects Revisited

the WCF runtime for a service creates a channel stack for each distinct address and binding combination used to communicate with the service .Each channel stack has a Channel Dispatcher object and one or more EndpointDispatcherobjects. The purpose of the Channel Dispatcher object is to determine which EndpointDispatcherobject should handle the message.

The role of the EndpointDispatcher object is to convert themessage into a method call and invoke the appropriate method in the service.

If add additional provided TCP endpoints for this service,then the WCF runtime would create two channel stacks (one for theHTTP endpoints, and another for the TCP endpoints), with their own ChannelDispatcherobjects. The TCP endpoints would have their own EndpointDispatcher objects. Each address and binding combination exposed by a servicecan be shared by multiple endpoints.

<services>
<servicename="Products.ProductsServiceImpl">
<endpointaddress="http://localhost:8010/ProductsService/Service.svc"
binding="ws2007HttpBinding"bindingConfiguration=""
name="WS2007HttpBinding_IProductsService"
contract="Products.IProductsService" />
<endpointaddress="http://localhost:8010/ProductsService/Service.svc"
binding="ws2007HttpBinding"bindingConfiguration=""
name="WS2007HttpBinding_IProductsService"
contract="Products.IProductsServiceV2" />
</service>
</services>
           

When the service receives a message on a channel, the ChannelDispatcher object at the top of the channel stack queries each of its associated EndpointDispatcher objects to determine which endpoint can process the message. If none of theEndpointDispatcher objects can accept the message, the WCF runtime raises theUnknownMessageReceived event on the ServiceHost object hosting the service.

EndpointDispatcher Objects and Filters

How does an EndpointDispatcher object indicate that it canprocess a message? An Endpoint Dispatcher object exposes two properties that theChannelDispatcher can query: AddressFilter and ContractFilter.

The AddressFilter property is an instance of the EndpointAddressMessageFilterclass.takes a message as its input parameter and returns a Boolean value that indicates whether the Endpoint Dispatcher object recognizes the address contained in the header of this message.

The ContractFilter property is an instance of theActionMessageFilter class.takes a message as its input parameter, and itreturns

a Boolean value that indicates whether theEndpointDispatcher object can handle the action specified in the message header.Remember that the action identifies the method that the EndpointDispatcher will invoke in the service instance if itaccepts the request.

It is also possible for more than one EndpointDispatcher object to indicate thatit can handle the message. In this case, the EndpointDispatcher class provides theFilterPriority property. This property returns an integer value.

If two matching endpoints have the same priority, the WCF runtimethrows a MultipleFilterMatches Exception exception. 

To summarize, the dispatching mechanism provides a highly customizable mechanism for determining which endpoint should process a message.

Routing Messages to Other Services

The WCF runtime makes it a relatively simple matter to build a WCF service that accepts specific messages and sends them to another service for processing.All you need to do is define a front-end service with a service contract that mirrors that of the target service.

public interface IRouter
{
[OperationContract(Action=”*”, ReplyAction=”*”)]
Message ProcessMessage(Message message);
}
           

If you specify a value of “*” for the Action property, theWCF runtime automatically routes all messages to this operation—regardless of the value of the action specified in the header of the message sent by theclient application

In this exercise, when the ShoppingCartClient applicationsends AddItemTo Cart, RemoveItemFromCart, GetShoppingCart, and Checkoutmessages to the Shopping

CartServiceRouter service, it will accept them all and theEndpointDispatcher will invoke the ProcessMessage method.

Usually, the WCF runtime on the service adds a ReplyAction based on the nameof the service and the operation. For example, the message that theShoppingCartService service sends back to a client application in response to an AddItemToCart messageis identified like this: http://adventure-works.com/2010/06/04/ShoppingCartService/AddItemToCartResponse

If you set the ReplyAction property of the OperationContractattribute to “*”, the WCF runtime for the service expects you to provide theappropriate ReplyAction in code and

add it to the message header when you create the responsemessage.

[ServiceBehavior(InstanceContextMode =InstanceContextMode.Single,
ValidateMustUnderstand = false)]
    public classRouter : IRouter
    {
        private staticIChannelFactory<IRequestChannel> factory = null;
        privateEndpointAddress address1 = new EndpointAddress(
       "http://localhost:9010/ShoppingCartService/ShoppingCartService.svc");
        private EndpointAddress address2 = newEndpointAddress(
       "http://localhost:9020/ShoppingCartService/ShoppingCartService.svc");
        private staticint routeBalancer = 1;
 
        staticRouter()
        {
            try
            {
                BasicHttpContextBinding service =new BasicHttpContextBinding();
               factory = service.BuildChannelFactory<IRequestChannel>();
               factory.Open();
            }
            catch(Exception e)
            {
                Console.WriteLine("Exception: {0}",e.Message);
            }
        }
 
        public MessageProcessMessage(Message message)
        {
           IRequestChannel channel = null;
           Console.WriteLine("Action {0}", message.Headers.Action);
            try
            {
                if(routeBalancer % 2 == 0)
                {
                   channel = factory.CreateChannel(address1);
                   Console.WriteLine("Using {0}\n", address1.Uri);
                }
                else
                {
                   channel = factory.CreateChannel(address2);
                   Console.WriteLine("Using {0}\n", address2.Uri);
                }
               routeBalancer++;
               message.Properties.Remove("ContextMessageProperty");
               channel.Open();
               Message reply = channel.Request(message);
               channel.Close();
                returnreply;
            }
            catch(Exception e)
            {
               Console.WriteLine(e.Message);
                returnnull;
            }
        }
    }
 
           
WCF 4 Step By Step Chapter14 Note 2 (Routing Messages)

Using the RoutingService Class

The purpose of the RoutingService class is to implementrequest routing based on the contents of the messages that it receives.

<system.serviceModel>
    <behaviors>
     <serviceBehaviors>
        <behaviorname="">
          <routingrouteOnHeadersOnly="true"filterTableName="ShoppingCartServiceRoutingTable"
             soapProcessingEnabled="false" />
       </behavior>
     </serviceBehaviors>
    </behaviors>
    <client>
      <endpointaddress="http://localhost:9010/ShoppingCartService/ShoppingCartService.svc"
         binding="basicHttpBinding" bindingConfiguration=""contract="*"
         name="ShoppingCartServiceHttpEndpoint1" />
      <endpointaddress="http://localhost:9020/ShoppingCartService/ShoppingCartService.svc"
         binding="basicHttpBinding" bindingConfiguration=""contract="*"
         name="ShoppingCartServiceHttpEndpoint2" />
    </client>
    <services>
      <servicename="System.ServiceModel.Routing.RoutingService">
        <endpointaddress="http://localhost:9000/ShoppingCartService/ShoppingCartService.svc"
           binding="basicHttpContextBinding" bindingConfiguration=""
           contract="System.ServiceModel.Routing.IRequestReplyRouter"/>
      </service>
    </services>
    <routing>
      <filters>
        <filtername="ShoppingCart1" filterType="Action"filterData="http://adventure-works.com/2010/06/04/ShoppingCartService/AddItemToCart"/>
        <filtername="ShoppingCart2" filterType="Action"filterData="http://adventure-works.com/2010/06/04/ShoppingCartService/RemoveItemFromCart”/>"
        <filtername="ShoppingCart3" filterType="Action"filterData="http://adventure-works.com/2010/06/04/ShoppingCartService/GetShoppingCart"/>
        <filtername="ShoppingCart4" filterType="Action"filterData="http://adventure-works.com/2010/06/04/ShoppingCartService/Checkout"/>
      </filters>
     <filterTables>
       <filterTable name="ShoppingCartServiceRoutingTable">
          <addfilterName="ShoppingCart1"endpointName="ShoppingCartServiceHttpEndpoint1"/>
          <addfilterName="ShoppingCart2"endpointName="ShoppingCartServiceHttpEndpoint1"/>
          <addfilterName="ShoppingCart3"endpointName="ShoppingCartServiceHttpEndpoint2"/>
          <addfilterName="ShoppingCart4" endpointName="ShoppingCartServiceHttpEndpoint2"/>
       </filterTable>
     </filterTables>
    </routing>
 </system.serviceModel>
           

if a message isreceived with an action of http://adventure-works.com/2010/06/04/ShoppingCartService/AddItemToCartin the header, the WCF runtime will route the message through the endpointidentified by the ShoppingCart1filter in the filter table. The endpointName property in this table refers to the name of the endpoint as defined in the <client>section of the configuration file.

With WCF, you can filter messages based on other criteria apart from the Action in the request header. For example, you can specify EndpointAddress to define an Endpoint AddressMessageFilter (the filterData property shouldidentify the endpoint address to match). If you wish to perform filtering based on the data in message bodies, you specify XPath to create an XPathMessageFilter object. The filterDataproperty defines the path to the data in the message and the value to matchagainst as an XPath expression.

Summary

You have seen how the WCF runtime for a service determines how tohandle an incoming message. The ChannelDispatcher object receiving the message querieseach of its EndpointDispatcher objects in turn. An EndpointDispatcher exposes theAddressFilter and ContractFilter properties that the ChannelDispatcher can use to ascertain whether theEndpointDispatcher can accept the message. The EndpointDispatcher selected to process themessage invokes the appropriate method in the service. You can customize the way in whichthe EndpointDispatcher accepts and processes messages by providing your own AddressFilterand ContractFilter objects and

implementing the IDispatchOperationSelector interface.

继续阅读