Tuesday, March 6, 2012

WCF Discovery In Depth

Service without discovery
Let us first look at a normal service call and then let us change it to make it 'discoverable'.
using (ServiceHost serviceHost = new ServiceHost(typeof(BlueCloudsService), baseAddress))
{
    // add endpoint of BlueClouds service
    serviceHost.AddServiceEndpoint(typeof(IBlueClouds), new WSHttpBinding(), string.Empty);

    // Open the ServiceHost to create listeners and start listening for messages.
    serviceHost.Open();
    // The service can now be accessed.
}

Discovery?
Discoverable = Clients can discover this service(BlueClouds). Again 'discover' seems to be a new term for web developers, so let us learn that quickly.
UDP is a multicast protocol with which you transmit once and is replicated by other networking components as needed and thus creates a broadcasting effect. So exploring/searching a service with UDP(or similar protocols) can be called discovery.
Making a service discoverable means helping it to be discovered using a protocol like UDP...simple! You can make it even simpler by having a centralised server(instead of UDP) called discovery proxy which clients and services could use. Latter is Managed and former is called Ad-hoc.

Teaser : Which all is correct?
             1) BlueClouds tell clients that it is discoverable.
             2) BlueClouds listen to calls from clients
             3) Clients ask(broadcast) if BlueClouds is present in the universe.
if  answer included 1 then will Blue clouds broadcast itself? what would it say: name/interface/url? should the service be online?
if answer includes 2, is it polling or event subscriber pattern?
if answer includes 3 then what question does it ask name/interface/url ? how did the client came to know about BlueClouds? should the service be online to be discovered?

To implement discovery two things are required.
#1 changes to make BlueClouds discoverable at server end
#2 changes at client's end to discover BlueClouds

#1 Steps to make BlueClouds discoverable at server end
1)  Add discovery behavior to the service
Add 'ServiceDiscoveryBehaviour' to Servicehost.behaviours.add()
2) Where should the service listen to? Or in otherwords add a endpoint.
Add 'UdpDiscoveryEndPoint' to serviceHost.AddServiceEndpoint()
using (ServiceHost serviceHost = new ServiceHost(typeof(BlueCloudsService), baseAddress))
{
    serviceHost.AddServiceEndpoint(typeof(IBlueClouds), new WSHttpBinding(), string.Empty);

    // ** DISCOVERY ** //
    // make the service discoverable by adding the discovery behavior
    ServiceDiscoveryBehavior discoveryBehavior = new ServiceDiscoveryBehavior();
    serviceHost.Description.Behaviors.Add(new ServiceDiscoveryBehavior());

   
    // ** DISCOVERY ** //
    // add the discovery endpoint that specifies where to publish the services
    serviceHost.Description.Endpoints.Add(new UdpDiscoveryEndpoint());
   
    // Open the ServiceHost to create listeners and start listening for messages.
    serviceHost.Open();

    // The service can now be accessed.
    }

Teaser clue: WCF Discovery allows a client to search for a service based on different criteria including contract types, binding elements, namespace, scope, and keywords or version numbers. WCF Discovery enables runtime and design time discovery. To make a service discoverable offline(eg: WCF in IIS does not run always ) use a proxy service for discovery and let the client contact the service directly. Should we select #2 as answer? not yet.. ;-)

And if want to enable discovery for a specific endpoint and not whole service then EndpointDiscoveryBehavior can be used


#2 changes to make Clients discover BlueClouds
1) decide a criteria to find namespace, binding, elements, etc.
         FindCriteria(typeof(IBlueClouds))
2) instantiate DiscoveryClient class with an endpoint (eg: udp)
            new DiscoveryClient(new UdpDiscoveryEndpoint());
3) find using DiscoveryClient object

            discoveryClient.Find(new FindCriteria(typeof(ICalculator)));

        DiscoveryClient discoveryClient =
            new DiscoveryClient(new UdpDiscoveryEndpoint());

        Collection<EndpointDiscoveryMetadata> blueCloudServices =
            discoveryClient.Find(new FindCriteria(typeof(IBlueClouds)));

        discoveryClient.Close();

        if (blueCloudsServices.Count == 0)
        {
            Console.WriteLine("\nNo services are found.");
        }
        else
        {
            serviceAddress = blueCloudsServices[0].EndpointAddress;

        }

That was easy! those two classes made things easy for us: ServiceDiscoveryBehaviour and DiscoveryClient. Shouldn't we go deeper and find out what happens behind the screen? Just for fun!  we do that soon..

BlueClouds Annoucement
Here is another feature. This is optional.
BlueClouds can be configured to send out announcement messages.... "here i am.. this is me.. there's nowhere..".
Again we have 2 things to do
1) Change BlueClouds code
2) Change subscriber(client) code
Change BlueClouds code
The ServiceDiscoveryBehavior class has a property called AnnouncementEndpoints.
discoveryBehavior.AnnouncementEndpoints.Add(new UdpAnnouncementEndpoint());
 the code looks like this:-
ServiceDiscoveryBehavior serviceDiscoveryBehavior = new ServiceDiscoveryBehavior();
serviceDiscoveryBehavior.AnnouncementEndpoints.Add(new UdpAnnouncementEndpoint());
serviceHost.Description.Behaviors.Add(serviceDiscoveryBehavior);


Change subscriber(client) code
The client needs to instantiate AnnouncementService class. And AnnouncementService has OnlineAnnouncementReceived and OfflineAnnouncementReceived events.

code:-
// Bring the subscriber online
serviceHost.Open();
// Create an AnnouncementService instance
AnnouncementService announcementService = new AnnouncementService();

// Subscribe the announcement events
announcementService.OnlineAnnouncementReceived += OnOnlineEvent;
announcementService.OfflineAnnouncementReceived += OnOfflineEvent;

// Create ServiceHost for the AnnouncementService
using (ServiceHost announcementServiceHost = new ServiceHost(announcementService))
{
    // Listen for the announcements sent over UDP multicast
    announcementServiceHost.AddServiceEndpoint(new UdpAnnouncementEndpoint());
    announcementServiceHost.Open();
}


And when a message is recieved this is what happens
static void OnOnlineEvent(object sender, AnnouncementEventArgs e)
{
    Console.WriteLine("Received an online announcement from {0}",
e.EndpointDiscoveryMetadata.Address);
}


Through config: Service
<behaviors>
      <serviceBehaviors>
        <behavior name="helloWorldServiceBehavior">
          <serviceDiscovery>
            <announcementEndpoints>
              <endpoint kind="udpAnnouncementEndpoint"/>
            </announcementEndpoints>
          </serviceDiscovery>
        </behavior>
      </serviceBehaviors>
<system.serviceModel>
   <services>
      <service name="HelloWorldService" behaviorConfiguration="helloWorldServiceBehavior">
         <!-- Application Endpoint -->
         <endpoint address="endpoint0"
                   binding="basicHttpBinding"
                   contract="IHelloWorldService" />
         <!-- Discovery Endpoints -->
         <endpoint kind="udpDiscoveryEndpoint" />
        </service>
    </service>
<!-- Announcement Listener Configuration -->
   <service name="AnnouncementListener">
      <endpoint kind="udpAnnouncementEndpoint" />
   </service>
Client
<discoveryClient>
            <endpoint kind="discoveryEndpoint"
                      address="http://localhost:8000/ConfigTest/Discovery"
                      binding="customBinding"
                      bindingConfiguration="httpSoap12WSAddressing10"/>
            <findCriteria duration="00:00:10" maxResults="2">
              <types>
                <add name="IHelloWorldService"/>
              </types>
              <scopes>
                <add scope="http://www.microsoft.com/building42/floor1"/>
              </scopes>           
            </findCriteria>
          </discoveryClient>

--End--(behind the screen/underpinning blog is here.. )comment your thoughts please :)




No comments:

Post a Comment