• Skip to main content
  • Skip to secondary menu
  • Skip to primary sidebar
  • Skip to footer
  • Home
  • Contact Us

All about code

  • Java
    • Spring
      • Spring Boot
      • Spring Cloud
    • Java Miscellaneous Tips
    • Java Design Patterns
  • Data Structures and Algorithms
    • Algorithms
    • Data Structures
  • Thoughts and Stories
  • AI

Spring Cloud: How to Implement Service Discovery (Part II)

binary technology informatics 1316395

Introduction

In the previous part of this article, Spring Cloud: How to Implement Service Discovery (Part I), we have seen the basics of Service Discovery in the context of Spring Cloud. We have seen that Netflix OSS Eureka component is still the main choice. In this post, we are going to discuss some Eureka additional topics, such as:

  • Java client API
  • REST API
  • Secure the server and the corresponding client side
  • Combine Service Discovery with Distributed Configuration

Service Discovery: Client Java API

In the examples in the first part of this article, the registering and fetching features were running under the hood and we have only seen the results of testing the whole architecture by calling a client REST endpoint. There is also a way to interact with the Eureka API programmatically, by using Java method calls. A possible choice would be to use the EurekaClient class. For example, if we want to get all the instances of a service identified by a particular ID, we could write the following code, supposing we have a client implemented as a Spring Boot application exposing REST services:

	@Autowired
	private EurekaClient eurekaClient;
		
	@GetMapping("/testEurekaClient")
	public String testEurekaClient() {
		Application application = eurekaClient.getApplication("CLIENT-SERVICE");
		List<InstanceInfo> instanceInfos = application.getInstances();
	    if (instanceInfos != null && instanceInfos.size() > 0 ) {
	        return instanceInfos.get(0).getHomePageUrl();
	    }
	    return null;
	}

Spring Cloud offers an alternative to the above with its own DiscoveryClient class. So, we can implement the above code also with the following:

	@Autowired
	private DiscoveryClient discoveryClient;
			         	
	@GetMapping("/testDiscoveryClient")
	public String testDiscoveryClient() {
	    List<ServiceInstance> serviceInstances = discoveryClient.getInstances("CLIENT-SERVICE");
	    if (serviceInstances != null && serviceInstances.size() > 0 ) {
	        return serviceInstances.get(0).getServiceId();
	    }
	    return null;
	}

Service Discovery: Eureka’s REST API

We have seen in the previous section how we can use a Java API to interact with the Eureka discovery server. In a more general scenario, we might have components not written in Java. In that case, we can use a REST API provided by Eureka. Some of its endpoints are described hereafter:

  • /eureka/v2/apps: with GET operation, gets all the instances.
  • /eureka/v2/apps/appID: if used with a POST action, it registers an application instance, and with a GET, it fetches all the application IDs.
  • /eureka/v2/apps/appID/instanceID: if used with a DELETE action, it removes the specific instance from the server’s registry, whereas a PUT sends a ‘heartbeat’ to the server for that particular instance.
  • /eureka/v2/apps/appID/instanceID/metadata?key=value: with a PUT serves the purpose of updating metadata.

Service Discovery: Securing the Server

Putting in place a minimum level of security is important for discovery instances in a production environment. We can protect the discovery server, for instance, by a simple authentication phase based on username and password. To do so, we should set a username and password in the application.yml file by the spring.security.user properties:

spring:
   security:
      user:
         name: myusername
         password: mypassword   

Then, in order to be able to authenticate and communicate with the service discovery server we must set something like the following on the client-side configuration:

eureka:
   client:
      serviceUrl:
         defaultZone: http://myusername:mypassword@localhost:8760/eureka/ 

Supposing the server port is 8760 and is running locally, along with the client.

Service Discovery: Securing the Client

The client side in this discussion is made by the service components of our microservice system. Securing them means securing the communication channels for each component. If the clients expose its features through REST services, the communication channel lies on HTTP protocol. We can secure it by enforcing SSL communication.

To enable SSL on the client side, we need a certificate in the first place. We can generate a self-signed certificate using the keytool Java program, like this:

keytool -genkey -alias client -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 365

Then we can take the generated keystore.p12 file and copy it into the /resources folder of our Spring Boot application and set the following piece of configuration in the application.yml file:

server:
   ssl:
      key-store: classpath:keystore.p12
      key-store-password: mypassword
      keyStoreType: PKCS12
      keyAlias: client

By the above, we can call our service using HTTPS in the protocol part of the URL. We can improve the configuration by enabling HTTPS and not HTTP, by setting the following Eureka instance properties:

eureka: 
   instance: 
      securePortEnabled: true 
      nonSecurePortEnabled: false 
      statusPageUrl: https://${eureka.hostname}:${server.port}/info 
      healthCheckUrl: https://${eureka.hostname}:${server.port}/health
      homePageUrl: https://${eureka.hostname}:${server.port}/

Here we have also set some endpoints, like the info, health, and the home page URL.

Service Discovery: Combine with Remote Configuration

We can combine service discovery with distributed configuration in two different ways:

  • Config First approach.
  • Discovery First approach.

We are going to describe these two approaches in the following sections.

Config First

In the Config First approach, the remote configuration server is the central point of the architecture. Both client and discovery service instances will connect to the config server in order to fetch their configuration.

To set up the configuration server, we have to add this dependency to the Maven POM file:

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-server</artifactId>
    </dependency>

In the application.yml file, to simplify the example, we are going to set the spring.profiles.active property as ‘native’. This way we can use a local repository, based on configuration files stored in the classpath, and we can choose to put them in a /config subfolder of the /resources Spring Boot application directory. We also secure the server with basic authentication by the security.user property.

spring:
  profiles:
     active: native
  application:
    name: config-server
  security:
    user:
      name: myusername
      password: mypassword

In the application.yml file of the client and discovery service applications, we will store just the basic properties. For instance, those required to connect to the configuration server. The rest of the properties will be stored on the configuration server itself, in specific files stored in the /config folder mentioned above.

Client Base Set Up

For the client service basic configuration, we have to add both the remote configuration and Eureka discovery client dependencies:

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>   
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>

And in the application.yml file, we will set the config.import parameter with the ‘optional:configserver’ value, in order to connect to the config server (no need to specify the http://localhost:8888 URL, since this is the default for the config server):

spring:  
  application:
    name: client-service
  config:
    import: "optional:configserver:"
  cloud:
    config:
      username: myusername
      password: mypassword

Discovery Server Base Set Up

The discovery server also needs the spring-cloud-starter-config dependency:

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
		</dependency>

And the application.yml content will be similar to that needed for the client:

spring:  
  application:
    name: discovery-service
  security:
     user:
        name: myusername
        password: mypassword
  config:
    import: "optional:configserver:"
  cloud:
    config:
      username: myusername
      password: mypassword

Specific Configuration for Client and Discovery Services

Having set the basic configuration of client and discovery components as above, we should set the more specific one inside the configuration server. We will use two configuration files stored in the /resources/config folder of the Spring Boot application mentioned before. The files are named after the application.name property of each application:

  • /config/client-service.yml
  • /config/discovery-service.yml

The content of client-service.yml will be:

server:
   port: ${PORT:8080}

myproperty: value

eureka:
  instance:
    hostname: localhost
  client:
    serviceUrl:
       defaultZone: http://myusername:mypassword@localhost:8760/eureka/

As we can see above, the defaultZone property contains the URL of the Eureka server instance, with username and password inside it, since we have secured the discovery server by basic authentication, as the configuration server.

As for the discovery server remote configuration, i.e. the discovery-service.yml file, we will have the following content:

eureka:
   instance:
      hostname: localhost
   client:
      registerWithEureka: false
      fetchRegistry: false
      
server:
   port: ${PORT:8760}

Config First: Sample Projects from GitHub

You can find three sample projects with the above configuration on GitHub:

  • Config Server
  • Client Service
  • Discovery Service

We can test the example by compiling and building the three projects with ‘mvn clean install’ and then executing the single jar files from the command line:

java -jar spring-cloud-discovery-configserver-configfirst-1.0-SNAPSHOT.jar
...
java -jar spring-cloud-discovery-client-configfirst-1.0-SNAPSHOT.jar
...
java -jar spring-cloud-discovery-server-configfirst-1.0-SNAPSHOT.jar

Then we can check the discovery service dashboard, where we will see the single client instance registered as ‘CLIENT-SERVICE’, and check the client, for instance, by executing some REST service implemented by it.

Discovery First

In the Discovery First approach, the discovery service is not supposed to store its configuration remotely. On the contrary, it is the config server that registers itself on the discovery registry. As for the client service, it will also register itself on the discovery service, in order to fetch the properties needed to connect to the config server. In this architecture, the client needs to know just the application ID of the config server and the security credentials, if there are any.

The discovery service does not need the config server client dependency anymore, and its configuration can be stored entirely in its local application.yml file:

spring:
   application:
      name: discovery-service
   security:
      user:
         name: myusername
         password: mypassword
               
eureka:
   instance:
      hostname: localhost
   client:
      registerWithEureka: false
      fetchRegistry: false
      
server:
   port: ${PORT:8760}

The client and config server local setups are described in the two sections below.

Client Base Set Up

The Maven POM dependencies are the same as the Config First scenario, while the basic local configuration in the local application.yml file must contain the URL to the Eureka service.

spring:
   application:
      name: client-service
      
eureka:
  client:
     serviceUrl:
        defaultZone: http://myusername:mypassword@localhost:8760/eureka/

Config Server Base Set Up

In addition to the spring-cloud-config-server , in the discovery first scenario, the config server application also needs the Eureka client dependency:

		<dependency>
		    <groupId>org.springframework.cloud</groupId>
		    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-config-server</artifactId>
		</dependency>

And the application.yml content will contain the discovery service URL, just like the client component:

server:  
  port: ${PORT:8888}
  
spring:
  profiles:
     active: native
  application:
     name: config-server
           
eureka:
  instance:
    hostname: localhost
  client:
    serviceUrl:
      defaultZone: http://myusername:mypassword@localhost:8760/eureka/

Specific Configuration for Client Service

In the discovery first scenario, the config server local repository will contain only the client configuration file:

  • /config/client-service.yml

And the content of client-service.yml will be:

server:
   port: ${PORT:8080}

myproperty: value

spring:
   cloud: 
      config: 
         discovery: 
            enabled: true 
            serviceId: config-server

eureka:
  client:
    serviceUrl:
       defaultZone: http://myusername:mypassword@localhost:8760/eureka/

Here, besides the discovery service URL, which is needed because the client has to connect first to it, we can also see the spring.cloud.config.discovery.enabled property set to ‘true’. This property makes the client application aware that it must connect to the config server by its metadata stored in the discovery service registry. The serviceId property stores the config server application ID, which matches that stored in the discovery service registry.

We can conclude that in the discovery service scenario, the config server instance (or instances) can be changed to run on a different host/port without the need to modify the client configuration.

Discovery First: Sample Projects from GitHub

You can find below the sample projects’ GitHub links for the Discovery First scenario:

  • Config Server
  • Client Service
  • Discovery Service

We can test the example as we already did for the config first scenario.

Conclusion

In this post, we have completed the discussion started in the first part of the article and introduced some further topics, such as:

  • Programmatic Java API.
  • REST API.
  • How to put a basic layer of security on the discovery server and client applications.
  • Combine discovery features and remote configuration in two different ways: config first and discovery first.

You can find the source code for the examples in this article in the following GitHub module:

  • Spring Cloud Discovery Examples Source Code

Note: the URL above actually is a submodule of a root repository that contains all the code for this blog, and in turn, is the root of other submodules with the specific examples of this article. From the standpoint of dependencies, it does not inherit from the general repository, though, and you can just take it and compile it as it is, or just take the sub-modules you are interested in.

Primary Sidebar

Social

  • Facebook
  • LinkedIn
  • Twitter

Archives

  • May 2026
  • April 2026
  • March 2026
  • August 2025
  • July 2025
  • June 2025
  • May 2025
  • April 2025
  • March 2025
  • December 2024
  • October 2024
  • August 2024
  • July 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • September 2023
  • May 2023
  • March 2023
  • January 2023
  • November 2022
  • September 2022
  • August 2022

Recent Posts

  • Better Avoid Spring Boot Lazy-initialization @PostConstruct Trap
  • How To Handle Large Datasets in Spring Boot
  • How to Log HTTP Incoming Requests in Spring Boot
  • How to Reliably Implement Post-Commit Actions in Spring
  • Brief and Easy Explanation of HATEOAS

TAGS

Algorithms Apache POI Backtracking Date Structures Dynamic Programming engineering Graphs Greedy Horror Stories HSSF Java Java New Features Java Principles Java tips real life Sorting Spring Boot Spring Cloud Strings Trees

Footer

Privacy Policy Cookie Policy Terms and Conditions