Skip to content

Implement your own service discovery mechanism

Stork is extensible, and you can implement your own service discovery mechanism. Stork uses the SPI mechanism for loading implementations matching Service Discovery Provider interface.

Dependency

To implement your Service Discovery Provider, make sure your project depends on:

<dependency>
    <groupI>io.smallrye.stork</groupI>
    <artifactId>smallrye-stork-api</artifactId>
    <version>SNAPSHOT</version>
</dependency>

Implementing a service discovery provider

Stork uses the SPI mechanism for loading implementations matching Service Discovery Provider interface during its initialization.As a consequence, a service discovery provider implementation will contain:

structure

The provider is a factory that creates an io.smallrye.stork.ServiceDiscovery instance for each configured service using this service discovery provider. A type, for example, acme, identifies each provider. This type is used in the configuration to reference the provider:

stork.my-service.service-discovery=acme

The first step consists of implementing the io.smallrye.stork.spi.ServiceDiscoveryProvider interface:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package examples;

import io.smallrye.stork.ServiceDiscovery;
import io.smallrye.stork.config.ServiceConfig;
import io.smallrye.stork.config.ServiceDiscoveryConfig;
import io.smallrye.stork.spi.ServiceDiscoveryProvider;

public class AcmeServiceDiscoveryProvider implements ServiceDiscoveryProvider {
    @Override
    public String type() {
        return "acme";
    }

    @Override
    public ServiceDiscovery createServiceDiscovery(ServiceDiscoveryConfig config,
                                                   String serviceName,
                                                   ServiceConfig serviceConfig) {
        return new AcmeServiceDiscovery(config.parameters());
    }
}

This implementation is straightforward. The type method returns the service discovery provider identifier. The createServiceDiscovery method is the factory method. It receives the instance configuration (a map constructed from all stork.my-service.service-discovery.attr=value properties)

Then, obviously, we need to implement the ServiceDiscovery interface:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package examples;

import io.smallrye.mutiny.Uni;
import io.smallrye.stork.DefaultServiceInstance;
import io.smallrye.stork.ServiceDiscovery;
import io.smallrye.stork.ServiceInstance;
import io.smallrye.stork.spi.ServiceInstanceIds;

import java.util.Collections;
import java.util.List;
import java.util.Map;

public class AcmeServiceDiscovery implements ServiceDiscovery {

    private final String host;
    private final int port;

    public AcmeServiceDiscovery(Map<String, String> configuration) {
        this.host = configuration.get("host");
        this.port = Integer.parseInt(configuration.get("port"));
    }

    @Override
    public Uni<List<ServiceInstance>> getServiceInstances() {
        // Proceed to the lookup...
        // Here, we just return a DefaultServiceInstance with the configured host and port
        // The last parameter specifies whether the communication with the instance should happen over a secure connection
        DefaultServiceInstance instance =
                new DefaultServiceInstance(ServiceInstanceIds.next(), host, port, false);
        return Uni.createFrom().item(() -> Collections.singletonList(instance));
    }
}

Again, this implementation is simplistic. Typically, instead of creating a service instance with values from the configuration, you would connect to a service discovery backend, look for the service and build the list of service instance accordingly. That's why the method returns a Uni. Most of the time, the lookup is a remote operation.

The final step is to declare our ServiceDiscoveryProvider in the META-INF/services/io.smallrye.stork.spi.ServiceDiscoveryProvider file:

examples.AcmeServiceDiscoveryProvider

Using your service discovery

In the project using it, don't forget to add the dependency on the module providing your implementation. Then, in the configuration, just add:

stork.my-service.service-discovery=acme
stork.my-service.service-discovery.host=localhost
stork.my-service.service-discovery.port=1234

Then, Stork will use your implementation to locate the my-service service.