Aug 29, 2009

WS-Discovery 101: Basic Discovery

I could talk at you all day about why this is cool and why you need this, but how about a sample, shall we?  To start, we’ll convert an existing simple Client/Server application to use discovery to allow the client to find its service.

Service Modifications

These are the modifications that need to be done to the service to get it to respond to probe requests.  It is 100% configuration driven (nice!).

Add Discovery Service Behavior

In the demo service, we were already exposing metadata using a service behavior, so we can just add the behavior to the existing behavior configuration

<behavior name="MyServiceBehavior">
    <serviceMetadata httpGetEnabled="true" httpsGetEnabled="false" />
    <serviceDiscovery />
</behavior>

So what we’ve done is instructed the service to publish endpoint information to clients that send a probe request, but we don’t get any use out of that until we tell the service what endpoints are important enough to tell anyone who asks about them.

Add Discovery Endpoint Behavior

The discovery endpoint behavior identifies those endpoints that need to be made available through WS-Discovery probe requests.

<system.serviceModel>
    <behaviors>
        <endpointBehaviors>
            <behavior name="PublishEndpoint">
                <endpointDiscovery />
            </behavior>
        </endpointBehaviors>

And here we are applying this behavior to the only endpoint we have:

<service behaviorConfiguration="MyServiceBehavior" name="BasicSample.Service.EchoService">
    <endpoint address="http://localhost:8080/EchoService/svc" 
              behaviorConfiguration="PublishEndpoint"
              binding="basicHttpBinding"
              name="httpEndpoint"
              contract="BasicSample.Service.IEchoService" />

Add UDP Endpoint for “Discoverers”

Without an endpoint to talk on, a service would never be free to handle incoming probe requests.  To do this, we simply add a UDP discovery endpoint longside the existing “httpEndpoint”.

<endpoint name="udpDisco" kind="udpDiscoveryEndpoint" />

That’s it!  When your service starts up, it’s ready to respond to WS-Discovery probe requests.

None of this is any good unless your client uses it.

Client Side Changes

Clients will send out a message to the network, looking for an appropriate service.  Unfortunately, this portion is not config-driven, however this process almost always will be slightly customized for the needs of the client, so it makes sense to do a bit of this discovery in code.

Remove the address and add a Dicovery Endpoint

First step is to prove to yourself that this works.  Delete the address!

When we are probing the network for a service, we need an endpoint capable of sending out these messages.

<client>
    <endpoint name="udpDisco" kind="udpDiscoveryEndpoint" />
    <!-- No address information! -->
    <endpoint binding="basicHttpBinding" contract="EchoProxy.IEchoService"
      name="BasicHttpBinding_IEchoService" />
</client>

Add Discovery Code

We need to add a reference in our client project to “System.ServiceModel.Discovery”.  This will give us access to a few classes we will need.

The only things left is to write the method that will search the network for the address of the service.  This will look something like this.

private static System.ServiceModel.EndpointAddress FindAddress()
{
    FindCriteria criteria = new FindCriteria(typeof(IEchoService));
    DiscoveryClient client = new DiscoveryClient();
    FindResponse response = client.Find(criteria);

    Console.WriteLine("Found {0} service(s).", response.Endpoints.Count);
    if (response.Endpoints.Count > 0)
    {
        return response.Endpoints[0].Address;
    }
    return null;
}

Notice here that we are searching the network for some pretty simple criteria (the service contract type) and that’s all.

Tying it all together

Now all we have to do is modify our typical client call to use the address that is returned from our FindAddress method.

private static void SendEcho(string textToEcho)
{
    EndpointAddress address = FindAddress();
    if (address != null)
    {
        using (EchoProxy.EchoServiceClient client = new EchoProxy.EchoServiceClient())
        {
            //use our newly found address
            client.Endpoint.Address = address;
            string result = client.Echo(textToEcho);
            Console.WriteLine(result);
        }
    }
}

That’s it!

Those of you following along at home will notice that this method takes some time to complete.  In the next post in the series, We’ll go over some ways you can optimize the time it takes for a probe to happen.

The code for this article is available here:

No comments:

Post a Comment