Spring Cloud Service Discovery with Dynamic Metadata

No Comments

Spring Cloud Service Discovery

If you are running applications consisting of a lot of microservices depending on each other, you are probably using some kind of service registry. Spring Cloud offers a set of starters for interacting with the most common service registries.

For the rest of this article, let’s assume you are familiar with the core Spring Boot ecosystem.

In our example we will use the service registry Eureka from Netflix running on localhost:8761. We will run two services service-1 on port 8080 and service-2 on port 8081 both implemented with Spring Boot. In order to register itself and look up others services, each service includes the following starter as a build dependency:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>Dalston.SR4</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
...
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>

Each service has to configure its unique name and where the Eureka server is located. We will use the YAML format in src/resources/application.yml:

spring:
  application:
    name: service-1

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka

The configuration of service-2 will look similar.

Service Discovery

The implementation of service-2 contains a simple REST controller to access the service registry:

@RestController
public class RegistryLookup {
 
   @Autowired
   private DiscoveryClient discoveryClient;
 
   @RequestMapping("/lookup/{serviceId}")
   public List<ServiceInstance> lookup(@PathVariable String serviceId) {
      return discoveryClient.getInstances(serviceId);
   }
 
}

Spring Cloud Service Discovery

In order to use the DiscoveryClient API, you have to use the annotation @EnableDiscoveryClient somewhere within your configuration classes.

A lookup of service-1 …

http://localhost:8081/lookup/service-1

… yields a lot of information on that service:

{
    "host": "SOME.IP.ADDRESS",
    "port": 8080,
    "secure": false,
    "instanceInfo": {
        "instanceId": "SOME.IP.ADDRESS:service-1:8080",
        "app": "SERVICE-1",
        ...

This information is used by REST clients like Feign to discover the HTTP endpoint of services by symbolic names like service-1.

Static Service Metadata

Each service can define arbitrary metadata within its configuration. With Eureka, this metadata can be a map of values defined in application.yml:

eureka:
  instance:
    metadata-map:
      fixed-s1: "value_1"

This metadata can be used by clients for various use cases. A service can describe SLAs, quality of data or whatever you want. But this metadata is fixed in a sense that is has to be known at build or deployment time. The latter is the case when you overwrite your Spring environment with JVM or OS environment variables. Anyway, the metadata show up within the lookup http://localhost:8081/lookup/service-1:

{
    "host": "localhost",
    "port": 8080,
    "uri": "http://localhost:8080",
    "metadata": {
        "fixed-s1": "value_1"
    },
    "secure": false,
    "serviceId": "SERVICE-1",
    ...
}

Dynamic Service Metadata

If a service wants to detect and register its metadata at runtime, you have to use the client API of the service registry in use. With the Eureka client this may look like this:

@Component
public class DynamicMetadataReporter {
 
   @Autowired
   private ApplicationInfoManager aim;
 
   @PostConstruct
   public void init() {
      Map<String, String> map = aim.getInfo().getMetadata();
      map.put("dynamic-s1", "value_2");
   }
}

The class ApplicationInfoManager is from the Eureka client API and allows you to dynamically add metadata that shows up if we query the service registry for service-1:

{
    "host": "localhost",
    "port": 8080,
    "uri": "http://localhost:8080",
    "metadata": {
        "dynamic-s1": "value_2",  // dynamic metadata
        "fixed-s1": "value_1"
    },
    "secure": false,
    "serviceId": "SERVICE-1",
    ...
}

Comment

Your email address will not be published. Required fields are marked *