Mike Taulty's Blog
Bits and Bytes from Microsoft UK
Indigo: Some rough notes on ServiceHost.Open()

Blogs

Mike Taulty's Blog

Elsewhere

 

This post is really just a collection of random notes I made the other day when I was staring at the simplicity of the following Indigo code;

 

[ServiceContract]

public class Printer

{

            [OperationContract]

            public void Print(string s)

            {

                        Console.WriteLine(s);

            }

}

class Program

{

            static void Main(string[] args)

            {

                        ServiceHost<Printer> p = new ServiceHost<Printer>(

                                    new Uri("net.tcp://localhost:9090"));

 

                        p.Open();

 

                        Console.ReadLine();

            }

}

 

Along with this configuration file;

 

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">

  <system.serviceModel>

    <services>

      <service serviceType="Printer">

        <endpoint address="printer" bindingSectionName="netProfileTcpBinding"  contractType="Printer"/>

      </service>

    </services>

  </system.serviceModel>

</configuration>        

 

and wondering what's actually going on when we make that call to ServiceHost<T>.Open() ?

 

Expect some errors and omissions here – as I say, these are just some rough notes.

 

Somewhere at the bottom of the stack I know that I’ve got a transport specific Listener picking up SOAP messages for me and somewhere at the top of the stack I know that I’ve got my class Printer but what happens in the middle to tie these things together?

 

I've had a play around with Reflector to see if I could understand where things happen and how they’re connected I’ve got a little bit of it but not all of it by any means right now. The stuff below is probably not anything you’d ever need to know but it might be interesting.

 

Construction

 

The ServiceHost<T> constructor chains up to the ServiceHost base class constructor which sets up a number of members;

 

            Base Addresses (UriSchemeKeyedCollection)

            Close Manager (CloseManager)

            Listener Factories (Dictionary<string, IListenerFactory>)

            Endpoint Listeners (EndpointListenerCollection)

            Extensions - (ExtensionCollection)

            Instances - (ServiceSite collection)

            Throttle - (ServiceThrottle)

           

And then the derived ServiceHost<T> constructor does a bunch of additional work, namely;

 

          ServiceLoader.LoadType(typeof(T)) uses a TypeLoader and creates a new ServiceDescription containing Behaviours, Validators and Endpoints.

 

          A new set of fairly vanilla looking security settings ServiceSecuritySettings get created.

 

ServiceLoader.InitializeDescription uses a ServiceReflector to build up the contractual information that’s buried in the code. There’s a lot of work done here and I haven’t explored it in any depth. The TypeLoader looks to help out a lot with reflection-heavy methods like CreateContractDescription, CreateOperationDescription, CreateMessageDescription.

           

The behaviors are also populated here by TypeLoader.AddBehaviors which again reflects around looking for IServiceBehavior attributes. Validators are also added with a bunch of validators baked into the code - MessagePatternValidator, InstancingValidator and so on. If a ServiceMetadataBehavior attribute is not already present somewhere then we add one so if you’re looking to add code to remove metadata generation then it looks like you should expect this attribute to already be there in the list.

 

With the service description initialized, it’s then extended/modified based upon the information in the configuration file. A ConfigLoader seems to take care of this in ConfigLoader.LoadServiceDescription which pulls information from the configuration file for the service.

 

ConfigLoader.LookupService is used to go from our service type (T) to a ServiceElement in the configuration file based upon assembly qualified and full names.

 

That element’s then opened up and all the endpoints within it are pulled out and for each one that endpoint tuple of (address, binding, contract) gets created. Additional behaviors look to be loaded from the config file if there’s any in there.

 

ServiceHost<T>.Open

 

This falls back into the base class implementation which is CommunicationObject.Open and essentially calls OnOpening(), OnOpen(), OnOpened(). A lot of things are CommunicationObjects in Indigo so you quickly get used to seeing them which is a nice part of the design.

 

Down in ServiceHost there’s a chain of activity. The big methods that I wandered across would be;

 

OnInitialize

 

Performs some validation. Then uses ServiceDescription.InitializeServiceHost which creates a DispatcherBuilder and calls InitializeServiceHost on it with the ServiceDescription and the ServiceHost.

 

This is an interesting method. Essentially it does something like;

 

foreach endpoint in the service description

 

Build an IListenerFactory implementation. This wanders through a number of classes like Binding, ChannelBuildContext but ultimately we get an implementation of IChannelBuilder and calling BuildListenerFactory on that. IChannelBuilder is implemented on all of the binding elements such as HttpTransportBindingElement and TcpTransportBindingElement. So, if you've configured an HttpTransportBinding then this framework will walk up that binding's implementation of IChannelBuilder and ask it to make a listener factory using IChannelBuilder.BuildListenerFactory and that’s how listeners will get made.

 

Create a new EndpointListener.

 

Use DispatcherBuilder.BuildDispatcher to create a dispatcher from the ServiceHost, ServiceDescription, ContractDescription. For each operation on the endpoint BuildDispatchOperation builds a DispatchOperation which looks to represent the mechanism used to ultimately invoke a method with a message - particularly through IOperationInvoker.Invoke.

 

Set the EndpointListener's Dispatcher property to the newly created dispatcher. As part of the set operation here the Dispatcher does an Attach to the EndpointListener and one of the things that happens there is that the Dispatcher hooks itself in to the Opened event on the EndpointListener.

 

OnBeginOpen

 

Loop through all the ListenerFactories and Open() on them.

Look through all the EndpointListeners and Open() on them.

 

Calling Open on an EndpointListener causes the Opened event to fire which the attached Dispatcher syncs up to. At the point where this happens (in the Dispatcher.EndpointOpened event handler) the Dispatcher wires up a couple of objects, a ListenerBinder and a ListenerHandler and ListenerHandler.OnOpen runs and calls ListenerHandler.NewChannelPump.

 

This makes use of a class called IOThreadScheduler to manage threading - IOThreadScheduler looks at first to be a reasonably simple work item queue with a routine which spins and pulls items off that queue and executes them. I haven't quite understood where this fits with respect to the .NET Thread Pool.

 

There’s a ChannelPump method which looks like a classic message loop for accepting incoming channels;

 

while (connections coming in && throttling conditions allow accept)

Dispatch();

 

The Dispatch method looks to do a bunch of behavioural work and ultimately ends up handing the new channel to a ChannelHandler instance and calling Register on it.

 

In a very similar pattern to the Dispatcher the ChannelHandler.Register method uses the IOThreadScheduler to run the MessagePump method on another thread.

 

It looks like the common path calls Receive which does either synchronous or asynchronous receiving from the channel. Either way we messages end up in HandleRequest which checks for throttling, dispatches the message through Dispatch and continues pumping.

 

What does Dispatch do? Firstly, it releases "ownership" on the pump, does some tracing and logging and then gets hold of the ProxyBehavior on the channel and grabs the DuplexCallbackDispatchBehavior, calls GetOperation on it to get a DispatchOperationRuntime and then calls Dispatch on that passing it an instance of MessageRpc that it's just created using the message, the runtime, the channel, the host and the thread. The dispatcher then seems to call MessageRpc.Process which is interesting.

 

MessageRpc.Process seems to have a chain of delegates that it's willing to invoke in order to get the message processed. Haven't quite worked this out yet but it sets up the first delegate in the chain and invokes it and, presumably, the next delegate in the chain can carry the chain on.

 

The implementation that I wandered into lives in DispatchRuntime in methods ProcessMessage1, 2, 3 through to 6. These seem to correspond to different stages in a message pipeline and you'd guess that they might get replaced by some kind of plug-in in the future if they're not already. Stages 4,5 and 6 look to be the one that gets an instance of the class providing the service through ServiceSite.GetInstance and DispatchOperationRuntime.

 

Haven't quite figured this out yet. But DispatchOperationRuntime.InvokeBegin calls into SyncMethodInvoker.Invoke and, in my case, that drops into my operation's implementation.


Posted Mon, May 2 2005 10:57 AM by mtaulty