Getting started with Spring Cloud: Service Discovery

In this tutorial, you’ll learn how to set up service discovery using Spring Cloud and Netflix Eureka. Service discovery is a key component of microservices architecture that allows services to discover and communicate with each other without hardcoding their network locations. We’ll create an Eureka Server for service registration and two microservices—Card Service and User Service—that will register with the Eureka server and interact with each other.

Project Structure

We’ll create three Spring Boot applications:

  1. Eureka Server: A service registry that keeps track of the available services.
  2. User Service: A simple microservice that provides user data.
  3. Card Service: A microservice that interacts with the User Service to retrieve user data.
spring cloud service discovery with eureka tutorial

Step 1: Create the Eureka Server

Let’s start by creating the Eureka Server, which will act as the service registry.

1.1 Create a Spring Boot Project

Add the necessary dependencies to your pom.xml or build.gradle.

Open your IDE and create a new Maven or Gradle project named eureka-server.

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

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

Replace ${spring-cloud.version} with the latest Spring Cloud version, e.g., 2023.0.0.

1.2 Enable Eureka Server

  • Open the main application class EurekaServerApplication.java and annotate it with @EnableEurekaServer.
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

	public static void main(String[] args) {
		SpringApplication.run(EurekaServerApplication.class, args);
	}
}

1.3 Configure Application Properties

  • Create an application.yml or application.properties file in the src/main/resources directory.

application.yml:

server:
  address: 0.0.0.0
  port: 8761

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false

spring:
  application:
    name: eureka-server

This configuration specifies that the Eureka Server will run on port 8761.

1.4 Run the Eureka Server

  • Run the EurekaServerApplication class.
  • Open a browser and navigate to http://localhost:8761. You should see the Eureka dashboard, showing that no services are currently registered.

Step 2: Create the User Service

Next, let’s create a simple UserService that will register itself with the Eureka Server.

2.1 Create a Spring Boot Project

  • Create a new Maven or Gradle project named user-service.
  • Add the necessary dependencies.
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
</dependencies>

2.2 Create the User Service Controller

  • Create a simple UserRegistrationController that performs a validation of the User based on the age. The UserRegistrationService contains the validation logic:
@RestController
@RequestMapping("/registration")
class UserRegistrationController {

    private final UserRegistrationService userRegistrationService;

    UserRegistrationController(UserRegistrationService userRegistrationService) {
        this.userRegistrationService = userRegistrationService;
    }

    @PostMapping
    ResponseEntity<User> registerUser(@RequestBody UserDto userDto,
                                      UriComponentsBuilder uriComponentsBuilder) {

        User user = userRegistrationService.registerUser(userDto);
        UriComponents uriComponents = uriComponentsBuilder.path("/users/{uuid}")
                .buildAndExpand(user.getUuid());

        return ResponseEntity.created(uriComponents.toUri()).body(user);
    }
}

@Service
class UserRegistrationService {

    User registerUser(UserDto userDto) {
        User user = new User(userDto);
        verifyUser(user);
        return user;
    }

    private void verifyUser(User user) {

        System.out.println("Check user age");
        if (user.getAge() > 18) {
            user.setStatus(User.Status.OK);
        } else {
            user.setStatus(User.Status.FRAUD);
        }
    }	
}

Then, to register the UserService application we will add the @EnableDiscoveryClient annotation in the main Application Class:

@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}

2.3 Configure Application Properties

  • Configure the application.yml file.
server:
  address: 0.0.0.0
  port: 9082

spring:
  application:
    name: user-service

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

This configuration specifies that the UserService will run on port 9082 and will register itself with the Eureka Server running on localhost:8761.

2.4 Run the User Service

  • Run the UserServiceApplication class.

Step 3: Create the Card Service

Now, we’ll create the CardService that will communicate with UserService via Eureka.

3.1 Create a Spring Boot Project

  • Create a new Maven or Gradle project named card-service.
  • Add the necessary dependencies.
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </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-starter-loadbalancer</artifactId>
  </dependency>
</dependencies>

3.2 Create a Client to Call User Service

  • Create a Client to interact with the UserService using Service Discovery
@Component
public class UserServiceClient {

	private final RestTemplate restTemplate;
	private final DiscoveryClient discoveryClient;


	UserServiceClient(@Qualifier("restTemplate") RestTemplate restTemplate,
			DiscoveryClient discoveryClient) {
		this.restTemplate = restTemplate;
		this.discoveryClient = discoveryClient;
	}

	public ResponseEntity<User> registerUser(CardApplicationDto.User userDto) {

		ServiceInstance instance = discoveryClient.getInstances("user-service")
				.stream().findAny()
				.orElseThrow(() -> new IllegalStateException("user-service unavailable"));

		return restTemplate.postForEntity(instance.getUri().toString()
						+ "/registration",
				userDto,
				User.class);
	}
}
How it works?
  1. DiscoveryClient: This is a tool that lets the application find other services registered with Eureka. In this case, it helps locate the user-service by its name.
  2. RestTemplate: This is a Spring utility that allows you to make HTTP requests. It’s like a web browser but in code; it lets you send data to another service or ask for data from it.
  3. ServiceInstance: When DiscoveryClient finds the user-service, it gives back information like the URL where the service can be accessed. This information is wrapped in a ServiceInstance object.

Besides, in order to use the RestTemplate, we will also need the following Configuration:

@Configuration
public class RestTemplateConfig {

	@Bean
	@LoadBalanced
	RestTemplate loadBalancedRestTemplate() {
		return new RestTemplate();
	}

	@Bean
	@Qualifier("restTemplate")
	RestTemplate restTemplate() {
		return new RestTemplate();
	}
}

@LoadBalanced: This annotation tells Spring to wrap the RestTemplate with a client-side load balancer

3.3 Create a Controller for the Card Service

The initial point of contact for our microservice ecosystem will be the Card Service Controller which acts as a gateway. It will intercept a call with the POST HTTP verb. Then, it will use the UserService Client to invoke the User Service.

@RestController
@RequestMapping("/application")
class CardApplicationController {

	private final CardApplicationService cardApplicationService;

	public CardApplicationController(CardApplicationService cardApplicationService) {
		this.cardApplicationService = cardApplicationService;
	}

	@PostMapping
	public ResponseEntity<ApplicationResult> apply(@RequestBody CardApplicationDto applicationDTO,
			UriComponentsBuilder uriComponentsBuilder) {
		CardApplication cardApplication = cardApplicationService
				.registerApplication(applicationDTO);
		UriComponents uriComponents = uriComponentsBuilder.path("/applications/{uuid}")
				.buildAndExpand(cardApplication.getUuid());

		return ResponseEntity.created(uriComponents.toUri())
				.body(cardApplication.getApplicationResult());
	}
}

3.4 Configure the Card Service

Include the following application.yml file which bind the Card Service to port 9080

spring:
  application:
    name: card-service

server:
  address: 0.0.0.0
  port: 9080

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

3.5 Run the Card Service

  • Run the CardServiceApplication class.

Connect to the Eureka Console and Test

Finally, when all applications are up and running, connect to the eureka Console at localhost:8761 and verify that the User Service and the Card Service are available:

spring cloud step-by-step tutorial eureka server

To test our application we will create a simple JSON Document which contains the User information. Namely, we will include the dateOfBirth which we check in the User Service:

{
  "user": {
    "name": "Mary",
    "surname": "Smith",
    "idNo": "ZXC123",
    "dateOfBirth": "1970-11-03"
  },
  "cardCapacity": 111
}

You can test it by sending the following POST Request to the Card Service:

curl -X POST -H "Content-Type: application/json" http://localhost:9080/application -d @cardApplication.json

You can verify that, with an age which is valid (18 or greater), the UserService will return a “valid” result. Otherwise it will return a “rejected”

{"status":"GRANTED"}

The full Source code for this tutorial is available here: https://github.com/fmarchioni/masterspringboot/tree/master/cloud/cloud-basic

Conclusion

In this tutorial, you learned how to set up a simple microservices architecture with Spring Cloud, using Netflix Eureka for service discovery. We created an Eureka Server, a User Service, and a Card Service that interact with each other through Eureka.

Found the article helpful? if so please follow us on Socials
Twitter Icon       Facebook Icon       LinkedIn Icon       Mastodon Icon