WS-Discovery is a multicast protocol used to discover
services within a network. It provides a standardized means of
discovering services at runtime by allowing services to locate other
services using UDP multicast messages or via a discovery proxy.
To find a target
service, a service consumer sends a probe message to a multicast group.
Target services that match the probe then send responses directly to the
service consumer informing it of their existence. A service consumer
can also look for endpoint address changes if a service’s network
location changed.
Version 4.0 of the
.NET framework introduced the WCF Discovery extension, a mechanism
based on the WS-Discovery industry standard. It enables service
redundancy, dynamic load balancing, and even fault tolerance (where if
one service fails to respond, another service that satisfies the
criteria can be dynamically located).
Being
able to discover services dynamically simplifies the maintenance of
service dependencies and increases the overall robustness of a service
inventory because an alternative to a failed or unavailable service can
quickly be located without explicit reconfiguration steps. Dynamic
discovery and announcements can also be necessary in highly dynamic
scenarios, such as in a dynamic cloud environment where new service
instances go online or offline, depending on current usage patterns.
Discovery Modes
WCF supports two discovery modes: ad hoc and managed:
Ad hoc discovery
allows services to locate other services on their local subnet and
announce their availability when they go online or offline. Each service
on the network can receive and respond to multicast discovery queries
for targeted discovery needs.
Managed discovery
introduces a dedicated discovery proxy into the environment. This proxy
manages service availability information and responds to discovery
queries to reduce the overall network traffic related to discovery
announcements and queries and further allows for the discovery of
services available outside the local subnet.
All classes related to the WS-Discovery protocol are encapsulated in the System.ServiceModel.Discovery library.
A service can respond to probe
messages by adding a well-known endpoint and service behavior to manage
the endpoint. As it is standard practice in WCF, both can be set
programmatically or via configuration files.
The UDP endpoint is a standard WCF endpoint. The next example shows the endpoint element adding a discovery endpoint. Specifically, the kind attribute (introduced with .NET 4.0) is used to identify the standard endpoints:
Example 1.
<services> <service name="Example.Services.CustomerService" behaviorConfiguration="DiscoveryBehavior">
... service application endpoint ...
<endpoint name="udpDiscoveryEpt" kind="udpDiscoveryEndpoint" /> </service> </services>
|
Note
that standard endpoints are configured machine-wide in the
Machine.Config file. You can override the configuration in the
Web.Config file, if necessary.
WCF 4.0 includes a service
behavior to manage probe requests received on the discovery endpoint.
This behavior is added to the service’s configuration, as shown here:
Example 2.
<behaviors> <serviceBehaviors> <behavior name="DiscoveryBehavior"> <serviceDiscovery /> </behavior> </serviceBehaviors> </behaviors>
|
A probe request includes a
number of “find” criteria, a service contract, or a service’s URL scope.
If a service matches all the find criteria, it responds with a FindMatch response containing its location, the matching criteria, and available metadata.
The probing service can
retrieve the metadata from the response and evaluate it to determine how
to handle the discovered service.
Shown here is an endpoint configuration with custom discovery metadata:
Example 3.
<endpoint address="" binding="basicHttpBinding" contract="ICustomerService" behaviorConfiguration="CustomMetadataBehavior" /> <endpointBehaviors> <behavior name="CustomMetadataBehavior"> <endpointDiscovery enabled="true"> <extensions> <MyCustomMetadata>Highly Scalable</MyCustomMetadata> </extensions> </endpointDiscovery> </behavior> </endpointBehaviors>
|
Locating a Service Ad Hoc
A
service consumer can send out WS-Discovery probe messages to locate
available services. The probe query can include compatibility criteria,
such as a service contract or a service scope. Query parameters are
encoded in a FindCriteria object.
There are two types of query criteria:
contract type names – searches for services with endpoints that implement the specified contract names
scope
– searches for services with endpoints that match the specified scope
(scopes are defined in the endpoint’s behavior and several matching
options for complete or partial matches exist)
The DiscoveryClient class provided by WCF manages probes and raises FindProgressChangedEventArgs events when ProbeMatch responses come in:
discoveryClient =
new DiscoveryClient(new UdpDiscoveryEndpoint());
discoveryClient.FindProgressChanged +=
new EventHandler<FindProgressChangedEventArgs>
(OnFindProgressChanged);
discoveryClient.FindCompleted +=
new EventHandler<FindCompletedEventArgs>
(OnFindCompleted);
discoveryClient.FindAsync
(new FindCriteria(typeof(ICustomerService)),
discoveryClient);
Following this, we are submitting an asynchronous discovery query over UDP. ParseResult examines the service contract, scope, and metadata to determine if the responding service meets the requirements:
Example 4.
private void OnFindProgressChanged( object sender, FindProgressChangedEventArgs e) { ParseResult(e.EndpointDiscoveryMetadata); }
|
The DiscoveryClient class also implements a synchronous Find() method that takes a FindCriteria
parameter that specifies query and query completion criteria, such as
the number of responses or the time to wait for responses.
DiscoveryClient also exposes a Resolve() method to locate a replacement for a service that’s been previously available at a known address. Calling Resolve() follows the same pattern as Find().
Sending and Receiving Service Announcements
Services can announce
their availability when they come online or go offline. These
announcements can be received by all services listening for them. You
configure a service to transmit announcements by adding announcementEndpoints to the serviceDiscovery behavior, as shown in Example 5. The service then issues announcements to the configured endpoint.
Example 5.
<behavior name="DiscoveryBehavior"> <serviceDiscovery> <announcementEndpoints> <endpoint name="udpEndpointName" kind="udpAnnouncementEndpoint"/> </announcementEndpoints> </serviceDiscovery> </behavior>
|
The standard udpAnnouncementEndpoint
is preconfigured in WCF. Each announcement includes the service
endpoint location and contract, as well as endpoint-specific metadata.
In highly dynamic
environments, service consumers may want to track available services
instead of probing for availability or relying on the discovery proxy.
Probing introduces additional latency and therefore should not occur as
part of the logic that resolves service
locations. Frequent multicast probing further results in unnecessary
network traffic. Instead, a consumer of dynamically available services
can listen for announcement broadcasts and maintain its own list of
services.
Services interested in receiving local UDP discovery announcements must open up a listener on the udpAnnouncementEndpoint endpoint. WCF provides a pre-built AnnouncementService class to handle service announcements. This class can raise the OnOnlineAnnouncement and OnOfflineAnnouncement events to the hosting application.
This example shows a listener configured for announcement services:
Example 6.
<services> ... application service information ...
<service name="AnnouncementListener"> <endpoint kind="udpAnnouncementEndpoint" /> </service> </services>
|
Next, we register events with AnnouncementService:
Example 7.
AnnouncementService announcementService = new AnnouncementService(); announcementService.OnlineAnnouncementReceived += new EventHandler<AnnouncementEventArgs> (this.OnOnlineAnnouncement); announcementService.OfflineAnnouncementReceived += new EventHandler<AnnouncementEventArgs> (this.OnOfflineAnnouncement);
|
The events receive an EndpointDiscoveryMetadata object, just like the response to a discovery probe. A FindCriteria object determines if the metadata matches endpoint requirements. In the following example, we query an EndpointDiscoveryMetadata announcement for compatibility with the ICustomerService contract:
Example 8.
private void OnlineAnnouncement (object sender, AnnouncementEventArgs e) { EndpointDiscoveryMetadata metadata = e.EndpointDiscoveryMetadata; FindCriteria criteria = new FindCriteria(typeof(ICustomerService)); if (criteria.IsMatch(metadata)) { ... further examine endpoint metadata ... ... store endpoint address for service access ... }
|
Discovery Proxies for Managed Discovery
Ad
hoc discovery is a suitable approach for static and smaller local
service networks, where all services live on the same subnet and
multicasting probes or announcements don’t add a lot of network chatter.
Larger environments, with services distributed across multiple subnets (or highly dynamic networks), need to consider a Discovery Proxy to overcome the limitations of ad hoc probing. A Discovery Proxy can listen to UDP announcements on the standard udpAnnouncementEndpoint for service registration and de-registration, but also expose DiscoveryEndpoint via a WCF SOAP binding.
The implementation
requirements for a Discovery Proxy can vary from a simple implementation
that keeps an in-memory cache of available services, to implementations
that require databases or scale-out solutions, like caching extensions
provided by Windows Server AppFabric. WCF provides a DiscoveryProxy base class that can be used for general proxy implementations.
Discovering from a Discovery Proxy
Discovering services registered
with a Discovery Proxy follows the steps for ad hoc discovery discussed
earlier. Instead of multicasting a UDP message, DiscoveryClient now needs to contact the Discovery Proxy. Details about the discovery contract are encapsulated in the DiscoveryEndpoint class. Its constructor only takes parameters for the communication protocol details, binding, and address.
Here we are configuring a discovery client to query the Discover Proxy:
Example 9.
DiscoveryEndpoint proxyEndpoint = new DiscoveryEndpoint( new NetTcpBinding(), new EndpointAddress(proxyAddressText.Text)); this.discoveryClient = new DiscoveryClient(proxyEndpoint);
|
Implicit Service Discovery
Our
coverage of WCF Discovery so far has focused on the explicit discovery
of services. However, it is worth noting that WCF Discovery can also
perform the same queries behind-the-scenes, when you configure endpoints
as DynamicEndpoint
programmatically or in the configuration file. This allows for highly
dynamic, adaptive environments where virtually no location-specific
details are maintained as part of code or configuration.
A client endpoint, for example,
can be configured to locate a service that matches on scope and
contract. In the following example, we configure DynamicEndpoint to locate a service with matching contract and metadata:
Example 10.
<client> <endpoint kind="dynamicEndpoint" binding="basicHttpBinding" contract="ICustomerService" endpointConfiguration="dynamicEndpointConfiguration" name="dynamicCustomerEndpoint" /> </client> <standardEndpoints> <dynamicEndpoint> <standardEndpoint name="dynamicEndpointConfiguration"> <discoveryClientSettings> <findCriteria duration="00:00:05" maxResults="1"> <types> <add name="ICustomerService"/> </types> <extensions> <MyCustomMetadata> Highly Scalable </MyCustomMetadata> </extensions> </findCriteria> </discoveryClientSettings> </standardEndpoint> </dynamicEndpoint> </standardEndpoints>
|
With this configuration, the service consumer can create a proxy object to the server with the following code:
Example 11.
ICustomerService svc =
new ChannelFactory<ICustomerService>
("dynamicCustomerEndpoint").CreateChannel();