Directory

Registering and discovering services with XOOM Directory.

Service Registration and Discovery

The XOOM Directory component supports service registration and discovery. When a new Bounded Context, implemented as a microservice, is started, it registers itself with the XOOM Directory.

As a result, other Bounded Contexts will discover the services that they must collaborate with. This happens when the XOOM Directory broadcasts the registration details, including hosts and ports, around the enterprise.

This is a cluster-capable component, which means it is meant to be run within the XOOM Cluster for reactive high availability. The cluster leader node is responsible for receiving registrations and broadcasting services information to listeners. Other nodes in the directory cluster maintain replicated registration information. If the leader node of the XOOM Directory fails, another node takes over leadership and takes over in receiving registrations and broadcasting. You should deploy each XOOM Directory cluster with at least three nodes, but five nodes would be even more robust.

Using the XOOM Directory

The following shows you how to register a service/application with the XOOM Directory and how to discover other services/applications that have registered with it. First configure the directory using the xoom-directory.properties file, which looks like this.

The different properties are explained next.

Setting Up a Directory

Property

Description

Default

directory.group.address

The directory service uses UDP to broadcast registration information to subscribers. This is the UDP group address for broadcasting.

237.37.37.1

directory.group.port

The port on which the directory.group.address operates.

37371

directory.incoming.port

On whatever IP address (possibly via a DNS domain name pointer) the directory service is running on, it will use this port number. The full address, including IP address and port, is broadcast by the directory service itself.

37399

directory.message.buffer.size

The size in bytes of the buffer used to process outgoing and incoming messages.

32767

directory.message.processing.interval

The interval (in milliseconds) between directory service message processing.

50

directory.unregistered.service.notifications

The number of times that the directory service will broadcast that a given service has unregistered.

20

Directory Client

The following provides the client perspective in interacting with the directory service. A client of the directory service must depend on the xoom-directory JAR file:

<dependency>
  <groupId>io.vlingo.xoom</groupId>
  <artifactId>xoom-directory</artifactId>
  <version>x.y.z</version>
</dependency>

Replace the version number x.y.z with the actual released version, such as 1.7.0.

Once configured you can start the DirectoryClient component, which is an Actor.

final DirectoryClient client =
  DirectoryClient.instance(stage, interest, group);
  
// or...

final DirectoryClient client =
  DirectoryClient.instance(stage, interest, group, maxMessageSize,
       processingInterval, processingTimeout);

The parameters are discussed in the the following table.

Parameter

Description

stage

The Stage in which the DirectoryClient actor should live.

interest

The ServiceDiscoveryInterest, which is a subscriber to service discovery information notifications provided by the directory.

group

The io.vlingo.xoom.wire.multicast.Group to join in order to receive broadcast notifications.

maxMessageSize

If overriding the default of 32767, provide this value.

processingInterval

If overriding the default of 50 milliseconds, provide this value.

processingTimeout

If overriding the default of 10 retries, provide this value. This is the number of times that the receiver attempts to read new incoming notification data before giving up, with additional read attempts following processingInterval.

After the DirectoryClient is started, you may send it messages to request registration of your service/application. This is accomplished using the register() message.

final ServiceRegistrationInfo info =
    new ServiceRegistrationInfo(
        "agilepm-context",
        Arrays.asList(
          new Location("1.2.3.45", 1111),
          new Location("1.2.3.46", 1111),
          new Location("1.2.3.47", 1111)));

directoryClient.register(info);

The above example registers the service named "agilepm-context" with three nodes, each on different servers with different IP addresses. The nodes available with your service are configured as the nodes within a XOOM Cluster. When the "agilepm-context" is shutting down it can unregister itself from the directory services. Unregistering will inform other services that the named service is not currently available.

directoryClient.unregister("agilepm-context");

The client must supply a ServiceDiscoveryInterest during the registration of theDirectoryClient. This ServiceDiscoveryInterest instance is responsible for informing its interests in any particular service, and in reacting to the notification of newly discovered services and recently unregistered services.

public class AgilePMServiceDiscoveryInterest extends Actor
    implements ServiceDiscoveryInterest {
  
  final List<String> myInterests =
    Arrays.toList("collaboration-service", "identity-access-service");
  
  final Map<String,ServiceRegistrationInfo> discoveredServices =
    HashMap<>();
  
  // ...
  
  @Override
  public boolean interestedIn(final String serviceName) {
    return myInterests.contains(serviceName);
  }

  @Override
  public void informDiscovered(final ServiceRegistrationInfo discoveredService) {
    if (interestedIn(discoveredService.name))
      discoveredServices.put(discoveredService.name, discoveredService));
    }
  }

  @Override
  public void informUnregistered(final String unregisteredServiceName) {
    discoveredServices.remove(unregisteredServiceName);
  }
}

In time the local service—the agilepm-service—will gain access to the services that it depends on—"collaboration-service" and "identity-access-service". This knowledge includes the IP addresses of the dependent services, which is one means to reach those services, such as with REST requests. How can this information be spread to all nodes in the service/application cluster? You can have a ServiceDirectoryInterest subscriber listening in every node. Or, you may decide to put the relevant ServiceRegistrationInfo into one or more cluster-wide attributes.

Implementation

The best place to see the implementation of the XOOM Directory service is in its main processing componentio.vlingo.xoom.directory.model.DirectoryServiceActor. The directory information is both maintained here and broadcast from this point. For this discussion see the following packages:

io.vlingo.xoom.cluster.model.application
io.vlingo.xoom.cluster.model.attribute
io.vlingo.xoom.directory.client
io.vlingo.xoom.directory.model
io.vlingo.xoom.wire.multicast

This is where XOOM Cluster and UDP multicasting meet. See the XOOM Wire component for its MulticastPublisherReader, which is alluded to in this discussion and used by the DirectoryServiceActor.

The DirectoryService protocol includes assignLeadership(), which is how a given XOOM Directory cluster node knows that it is in the lead and is the active UDP multicaster/publisher. Any other cluster nodes are standby in case the leader node is lost for any reason.

A DirectoryClient, which is any service that wants to register and/or discover via the XOOM Directory, sends and receives availability information in the form of ServiceRegistrationInfo by supporting the ServiceDiscoveryInterest protocol.

Ultimately the io.vlingo.xoom.directory.model.DirectoryApplication, which is a ClusterApplication via the ClusterApplicationAdapter, informs its own DirectoryServiceActor to go into either active or passive mode. Active mode is described above. Passive mode is any cluster node member that is not the leader. These (most typically 2 of 3 nodes in the cluster) simply collect publishable data that the leader receives. The leader tells them this via the cluster AttributesProtocol attributesClient. This means that the leader uses the cluster-wide attribute publishing facility to share registration information with all passive nodes. If the leader is lost, then a passive node will be elected as the leader, and already has most or all of the current registration information. Any lag in current registration information will soon be cleared up when directory clients begin live (pulse-based) updating to the new directory leader.

Last updated