Skip to content
xuanzhong edited this page Apr 7, 2015 · 36 revisions

##Overview

A Service Directory facility is an essential requirement of a Service Oriented Architecture (SOA). It provides the basis for a Service Provider to register its Service Endpoints such that the attributes required to communicate with the endpoints can be discovered by a Service Consumer which needs to use the service:

Service Directory Overview

###Service Directory Concepts

Some of the concepts and terms used are defined below.

####Service Directory

This is our name for the infrastructure facility providing the aggregate of registration and lookup features.

####Service

An abstraction for a logical business process, uniquely named. The components in a distributed system interact with the service by learning about a specific instance of the service to communicate with. Each instance is implemented by a Service Provider application (which may implement many different service instances). The interface to the service instance is called a Service Endpoint; its representation is standardly encoded in URI format.

####Directory Server

An application, a component of the Service Directory facility, which provides distributed persistence for the Service Directory service instance registrations, plus various lookup features, for its clients. The server is responsible for tracking the health of registered service instances (those intended to be monitored), and to provide means to make service availability visible to its clients, as required. This may include aggregate service availability, as well as instance availability state.

####Service Directory Client

An application which interacts with a Directory Server according to one any of these roles:

  • a Service Directory Provider, which registers one or more services implemented by the application itself;
  • a Service Directory Consumer, which looks up services by name, to obtain information about Service Endpoints it should communicate with;
  • a Service Directory Proxy Provider, a weakened notion of a provider, responsible for registering one or more services which are implemented externally.

####Service Directory API

A library component of the Service Directory facility, providing means for a client to fulfill any (or all) of the above roles.

Beyond providing a convenient, callable interface hiding the details of the communications protocols between client and Directory Server, the API includes a performance-boosting service cache (for highly efficient lookups by consumers), and an efficient heartbeat mechanism for monitoring the availability of provided services. A ServiceDirectory class (static usage, only) provides the entry point for using the features of the Service Directory API, including: configuration, getting references to the functional interfaces for the main Service Directory client roles, and resetting the API (for unit testing).

####Service Instance

A Service Instance is a representation of a named Service Endpoint owned by a Service Provider. It is represented in the API by the class ServiceInstance. ServiceInstance has the name, instance id, URI and metadata info. Principally, once having looked up a Service and retrieving a specific ServiceInstance, the client acting as Service Consumer would use the URI to invoke the function supplied at the specific endpoint of that instance.

Multiple instances of a named service may exist, each sharing the common service name, but with different attributes. Attributes may be intrinsic, modifiable, or provider-defined metadata, and may include:

  • the Service Name, common to all instances of the service (intrinsic);
  • the identity of the application implementing the service instance, i.e. the Service Provider (intrinsic);
  • the Service Endpoint, a URI specifying the means for a client to communicate with the instance (modifiable);
  • the known status of the instance: Up, Down, Unmonitored (modifiable);
  • additional attributes in the form of service meta-data, which may be used to refine the lookups to obtain a subset of service instances.

####Registration Manager

That part of the Service Directory API which supports the roles of Service Directory Provider (and Proxy Provider). The RegistrationManager interface provides methods to register, update and unregister a ServiceInstance. The API's implementation class is responsible for all communications with the Directory Server in support of these features. This includes an optimized heartbeat model to regularly assert the availability of the provider's registered (and monitorable) instances.

####Lookup Manager

That part of the Service Directory API which supports the roles of Service Directory Consumer. The LookupManager interface provides methods for simple ServiceInstance lookups as well as more refined queries. A change notification interface allows a client application to register for a callback when the state of an instance of a specified service has changed.

The API's implementation class is responsible for all comm maintaining the cache of instances previously requested by lookups or queries, based on dynamic change notifications from the Directory Service.

Single-instance lookup supports a default rotational selection mechanism, providing a default client-side load balancing feature.

####Unit Test Framework

It is convenient for application unit testing to not require full-blown integration with an actual Directory Service. In normal API usage, it returns RegistrationManager and LookupManager objects created by an internally initialized default ServiceDirectoryManagerFactory implementation. Their functionality depends on communicating with an actual Directory Service. For unit testing, a client project can use an alternative factory (included in the API), to obtain RegistrationManager and LookupManager objects with a different implementation, maintaining all state in memory thus precluding need to communicate with a DirectoryServer. Applications may extend the alternative implementation to mix-in their own test logic (initializations, assertions, etc.)

##Using the Service Directory API

The preferred project structure for Foundation uses Maven project object model (POM) files to specify how to build and package a project.

The OSS v1.x SD API requires setting up a v1.x Directory Server. For a client project to use the Service Directory API V1.x, the SD API library is specified in the dependency section of the project's pom.xml file:

<dependency>
    <groupId>com.cisco.oss.foundation.directory</groupId>
    <artifactId>sd-api</artifactId>
    <version>1.1.0-6</version>
</dependency>

SD API V1.1 is compatible with JDK6 or greater, to support legacy application integration. This version uses no features requiring support from a more modern Java version. SD API V1.2 is compatible with JDK7.

The following Maven repository for the SD snapshot should be put into your own settings.xml file.

https://oss.sonatype.org/content/repositories/snapshots/com/cisco/oss/foundation

The release versions of the SD API can be found here.

If you are using Ant for your projects, you can download the SD API jars with dependency libraries as a tar ball (sd-api-version.tar.gz): from here.

###API Use Cases

Before using the Service Directory API to connect to the directory server, the API needs to be informed of the location of the Directory Service. Two main configuration settings are used to communicate this to the API:

The server address: DirectoryServiceClient.SD_API_SD_SERVER_FQDN_PROPERTY, which has default value vcsdirsvc.

The server port: DirectoryServiceClient.SD_API_SD_SERVER_PORT_PROPERTY, which has default value 2013.

If DirectoryServer is using the default port value, then you only need to insure the server address (first property above) works for your environment.

There are three ways the configuration settings can be made to work for your environment:

a. Define a host alias vcsdirsvc to resolve to the IP where the Directory Service runs. Some production deployments will probably use a DNS (especially where HA Directory Server clusters are fronted by a load balancer exposing a virtual IP). This can also be accomplished by defining the host alias in the /etc/hosts file with the DIR-SVC-IP value, by adding a line like this:

DIR-SVC-IP  vcsdirsvc

In this case, the default address setting will map to the DN which resolves to the desired IP.

b. Another way is to add a file named config.properties in your Java classpath (or found using a path which can be specified to the API - see API javadoc for details). This needs to have (at least) these lines:

server.fqdn=SERVER-IP-OR-HOSTNAME
server.port=SERVER-PORT

C. A third way is to use the configuration features of Service Directory API to directly set properties for Directory Service IP or hostname (if it is resolvable), and/or the port number, in code:

ServiceDirectory.getServiceDirectoryConfig().setProperty(
    DirectoryServiceClient.SD_API_SD_SERVER_FQDN_PROPERTY, "SERVER-IP-OR-HOSTNAME");
ServiceDirectory.getServiceDirectoryConfig().setProperty(
    DirectoryServiceClient.SD_API_SD_SERVER_PORT_PROPERTY, SERVER-PORT);

The only real advantage of this approach is that your application will now control where the values for SERVER-IP-OR-HOSTNAME and SERVER-PORT come from (i.e. your own property definitions).

Note: A static shutdown() method is provide for the ServiceDirectory class. If ServiceDirectory.shutdown() is called, the SD API will be completely shut down. A user application needs to restart the JVM to reload the ServiceDirectory class.

1. Service registration

// Get a RegistrationManager instance from ServiceDirectory.    

// The ServiceDirectory will load a default ServiceDirectoryConfig and instantialize a RegistrationManager instance.    

RegistrationManager registrationManager = ServiceDirectory.getRegistrationManager();    

// The name of the service    

String serviceName = "odrm-setupsession";    

// The address is the public ip address or the host name of the machine which runs the service.    

String address = "10.10.35.7";    

// The port of the service.    

String port = "8090";    

// Construct the service instance. serviceName and providerId together uniquely identify a service instance where providerId is defined as "address-port".    

ProvidedServiceInstance instance = new ProvidedServiceInstance(serviceName, address, port);    

// Setting the service instance URI. URI is defined as tcp://address:port for the TCP end point    

instance.setUri("http://odrm.cisco.net:8090/ndvr/setupsession");    

// Setting the service instance metadata    

Map<String, String> meta = new HashMap<String, String>();    

meta.put("version", "2.5.0");    

meta.put("datacenter", "datacenter1");    

meta.put("region", "east");    

instance.setMetadata(meta);    

// By default, the instance status is DOWN    

instance.setStatus(OperationalStatus.UP);    

// healthCallback is optional, leave it to be null when registering, if the service should not be monitored,     

//e.g. the provider is acting as a proxy, or otherwise chooses not to be monitored.    

ServiceInstanceHealth healthCallback = new ServiceInstanceHealth(    

    public boolean isHealthy(){    
    // implementation, skip here.    
    return true;    
    }    

);    

registrationManager.registerService(instance, healthCallback);    


// Update the OperationalStatus of the instance to DOWN, this instance will be removed from lookup.    

registrationManager.updateServiceOperationalStatus(serviceName, instance.getProviderId(), OperationalStatus.DOWN);    


// Update the instance: OperationalStatus to UP, and the "version" metadata to "2.5.1"    

instance.setStatus(OperationalStatus.UP);    

instance.getMetadata().put("version", "2.5.1");    

instance.getMetadata().put("datacenter", "dc01");    

registrationManager.updateService(instance);    


// Unregister the instance.    

registrationManager.unregisterService(serviceName, instance.getProviderId());    

####2. Service lookup

// Get the LookupManager from ServiceDirectory.

LookupManager lookupManager = ServiceDirectory.getLookupManager();

String serviceName = "odrm-setupsession";

// Simple service lookup, an available service instance is returned via Round-Robin policy

// from the list of the ServiceInstances

ServiceInstance serviceInstance1 = lookupManager.lookupInstance(serviceName);

// Get service endpoint uri

String uri = instance.getUri();

// Look up all ServiceInstances of the Service.

List<ServiceInstance> allServiceInstances = lookupManager.getInstances(serviceName);

// Query ServiceInstance, i.e. filtering the ServiceInstance via some query criteria on the service metadata.

//Here is an example on how to query the ServiceInstance by a certain version.

ServiceInstanceQuery query = new ServiceInstanceQuery().getEqualQueryCriterion("version", "1.0").getEqualQueryCriterion("datacenter", "dc01");

// Add a EqualQueryCriterion, it returns an available ServiceInstance which has the version equals to 1.0.

ServiceInstance versionedServiceInstance = lookupManager.queryInstance(serviceName, query);

// Query all ServiceInstances which have the version equals to 1.0.

List<ServiceInstance> queryedServiceInstances = lookupManager.queryInstances(serviceName, query);

3. Using the Unit Test framework

the application can cause the API to use an alternate manager factory implementation, , before otherwise using the API. This will force the API to use an in-memory registration facility. It is also possible for the application's unit tests to extend the default test manager (or provide a wholly different test manager).

The API includes some hooks for unit testing. The basic notion is to cause the SD API use an internal test manager which combines the responsibilities of both the RegistrationManager and LookupManager interfaces. To do this, a DefaultTestServiceDirectoryManager class is included in the API. This is the default returned by the alternate manager factory implementation TestServiceDirectoryManagerFactory. When the SD API is configured to use the alternate factory, with DefaultTestServiceDirectoryManager, the the following happens:

  • the default client manager, responsible for all communications with a real Directory Server, will be bypassed;
  • the internal cache, normally subservient only to the LookupManager implementation (and updated by the client manager and its helper classes), will be exposed internally for direct manipulation by the RegistrationManager implementation.

DefaultTestServiceDirectoryManager may be extended (requiring injection of the specific subclass into the TestServiceDirectoryManagerFactory), to mixin customized test behaviors required for the project under test.

Example usages of the unit test features are described below.

Out-of-the-box unit test example

The SD Client API uses a factory class (implementation of {{ServiceDirectoryManagerFactory}}) to initialize its implementation, and make available references to the classes which implement the key interfaces used by client code. So using the unit test support merely requires using a different factory implementation.

One is provided for you. TestServiceDirectoryManagerFactory will construct and initialize DefaultTestServiceDirectoryManager, providing an out-of-the-box unit test capability for you. The example below shows how to use this.

// (re)initialize the unit test framework using the TestServiceDirectoryManagerFactory. ServiceDirectory.reinitServiceDirectoryManagerFactory(new TestServiceDirectoryManagerFactory());

// Set some configuration properties for testing - e.g. use a shorter cache sync interval ServiceDirectory.getServiceDirectoryConfig().setProperty( "cache.sync.interval", "1");

// Perform registration or lookup RegistrationManager registrationManager = ServiceDirectory.getRegistrationManager(); // Register some service, omitted

LookupManager lookupManager = ServiceDirectory.getLookupManager(); // Look up some service, omitted

Each time you call ServiceDirectory.reinitServiceDirectoryManagerFactory(...), the any currently in use client API implementation classes will be released, and a new DefaultTestServiceDirectoryManager constructed and initialized. This allows episodic, clean, unit tests.

Customized unit test example

For more elaborate unit tests, you may want an instrumented manager.

This is possible by extending DefaultTestServiceDirectoryManager. E.g. you might want to override provided methods to inject your logging/instrumentation (but still using the provided methods of DefaultTestServiceDirecoryManager for the basic unit test behavior). You might also want to provide extra methods to return references to, say the internal cache, etc. to facilitate test assertions.

Either way, you force the use of your enhanced DefaultTestServiceDirecoryManager by passing a reference to your constructed instance, to the alternative constructor of TestServiceDirectoryManagerFactory. E.g.

// Create the customized test manager instance (override of DefaultTestServiceDirectoryManager) DefaultServiceDirectoryManager myTestManager = new MyServiceDirectoryTestManager( /* any additional args */ );

// (re)initialize the unit test framework, forcing TestServiceDirectoryManagerFactory to use your enhanced manager ServiceDirectory.reinitServiceDirectoryManagerFactory(new TestServiceDirectoryManagerFactory(myTestManager));

Clone this wiki locally