vlingo/directory

Registering and discovering services with vlingo/directory.

Service Registration and Discovery

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

As a result, other Bounded Contexts will discover the services that they must collaborate with. This happens when the vlingo/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 vlingo/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 vlingo/directory fails, another node takes over leadership and takes over in receiving registrations and broadcasting. You should deploy each vlingo/directory cluster with at least three nodes, but five nodes would be even more robust.

How To

The following shows you how to register a service/application with the vlingo/directory and how to discover other services/applications that have registered with it.

Setting Up a Directory

First configure the directory using the vlingo-directory.properties file.

directory.group.address = 237.37.37.1
directory.group.port = 37371
directory.incoming.port = 37399
# the maximum size of a message
directory.message.buffer.size = 32767
# the interval for processing the channel
directory.message.processing.interval = 50
# the interval for publishing my availability and services registration
directory.message.publishing.interval = 5000
# the number of times to publish that a service has unregistered
directory.unregistered.service.notifications = 20

The following table provides an explanation for each of the configuration values.

Property

Description

directory.group.address

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

directory.group.port

The port on which the directory.group.address operates. The default is 37371.

directory.incoming.port

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

directory.message.buffer.size

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

directory.message.processing.interval

The time interval in milliseconds between directory service message processing. The default is 50 milliseconds.

directory.unregistered.service.notifications

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

Directory Client

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

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

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

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.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 addition 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 vlingo/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 interest to 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);
}
private boolean interestedIn(final String serviceName) {
return myInterests.contains(serviceName);
}
}

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 vlingo/directory service is in its main processing componentio.vlingo.directory.model.DirectoryServiceActor. The directory information is both maintained here and broadcast from this point. For this discussion see the following packages:

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

This is where vlingo/cluster and UDP multicasting meet. See the vlingo/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 vlingo/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 vlingo/directory, sends and receives availability information in the form of ServiceRegistrationInfo by supporting the ServiceDiscoveryInterest protocol.

Ultimately the io.vlingo.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.