spring

Simplified Routing via Spring Cloud

Spring Cloud is a framework that simplifies building and managing cloud-native applications in the Java ecosystem. It offers a set of tools and libraries for creating scalable, resilient, and distributed microservices architectures. With features like service discovery, load balancing, configuration management, and fault tolerance, Spring Cloud enables developers to easily address common challenges when developing applications for the cloud. It promotes the use of Spring Boot, making it easier to develop, deploy, and maintain microservices-based applications. Spring Cloud fosters modularity, flexibility, and ease of integration, allowing developers to focus on business logic while ensuring robust and efficient cloud-native solutions. Let us explore Simplified Routing via Spring Cloud.

1. Introduction

Spring Cloud is a powerful framework that simplifies the development and management of cloud-native applications in the Java ecosystem. It provides a wide range of tools and libraries to address common challenges associated with building scalable, resilient and distributed microservices architectures.

1.1 Benefits of Spring Cloud

Spring Cloud offers several key benefits to developers and organizations:

  • Simplified Microservices Development: Spring Cloud makes it easier to develop microservices by providing abstractions and common patterns for distributed systems. Developers can focus on writing business logic instead of dealing with infrastructure concerns.
  • Service Discovery and Registration: It includes tools like Eureka and Consul for service discovery, enabling services to find and communicate with each other dynamically. This helps in building flexible and scalable architectures.
  • Load Balancing: Spring Cloud integrates with load balancers like Ribbon, distributing incoming requests evenly across multiple instances of a service to improve performance and availability.
  • Configuration Management: Spring Cloud Config simplifies managing configurations for microservices, allowing for centralized configuration updates without requiring redeployment.
  • Fault Tolerance: With tools like Hystrix, Spring Cloud provides mechanisms for handling failures gracefully, preventing cascading failures in a microservices environment.

1.2 Use Cases of Spring Cloud

Spring Cloud is suitable for a wide range of use cases:

  • Microservices Architecture: Spring Cloud is tailor-made for building microservices-based applications. It facilitates the development of loosely coupled, independently deployable services that work together seamlessly.
  • Cloud-Native Applications: Organizations looking to develop and run applications in cloud environments can leverage Spring Cloud for building applications that are highly scalable, resilient, and cloud-ready.
  • Enterprise Integration: Spring Cloud can be used to integrate and orchestrate services across different departments or teams within an enterprise, improving communication and data flow.
  • API Gateways: It’s employed to build API gateways that manage incoming requests, perform authentication, route requests to appropriate services, and handle traffic control.
  • IoT and Edge Computing: Spring Cloud’s distributed and fault-tolerant capabilities make it a suitable choice for IoT and edge computing applications, where reliability and scalability are crucial.

Spring Cloud is a versatile framework that empowers developers to navigate the complexities of building and managing cloud-native applications efficiently. With its robust toolset and proven patterns, it’s a valuable addition to the toolkit of any Java developer venturing into the world of microservices and cloud computing.

2. What is the Eureka Discovery Server and Discovery Client?

Eureka is a key component of the Spring Cloud ecosystem, providing service registration and discovery capabilities. It consists of two main parts: Eureka Discovery Server and Eureka Discovery Client.

2.1 Eureka Discovery Server

The Eureka Discovery Server, simply referred to as the “Eureka Server,” is a service registry. It’s responsible for maintaining a registry of all the microservices (or “services”) that are running in a distributed system. When a microservice starts up, it registers itself with the Eureka Server, providing essential information such as its hostname, port, and health status. Key features and responsibilities of the Eureka Server include:

  • Service Registration: Microservices register themselves with the Eureka Server when they start. This registration includes metadata that allows other services to discover and communicate with them.
  • Service Discovery: Other microservices can query the Eureka Server to discover the available services and their network locations. This enables dynamic and resilient communication between services.
  • Health Monitoring: Eureka Server periodically checks the health status of registered services. If a service becomes unavailable (e.g., due to a crash), the Eureka Server marks it as “out of service.”
  • Load Balancing: While Eureka itself doesn’t perform load balancing, it integrates with other components like Netflix Ribbon (a client-side load balancer) to help balance the load across multiple instances of a service.

2.2 Eureka Discovery Client

The Eureka Discovery Client is a library integrated into microservices that need to discover and communicate with other services. These microservices are referred to as “Eureka Clients.” Each Eureka Client communicates with the Eureka Server to register itself and discover the locations (hostnames and ports) of other services. Key responsibilities and benefits of the Eureka Discovery Client include:

  • Service Registration: When a microservice starts, it uses the Eureka Discovery Client to register itself with the Eureka Server. This registration provides information that allows other services to locate and connect to it.
  • Service Discovery: Eureka Clients can query the Eureka Server to discover the network locations of other services. This dynamic discovery enables resilient communication between services, even as instances of services come and go.
  • Load Balancing: Eureka Clients can integrate with load balancers like Netflix Ribbon, which helps distribute incoming requests across multiple instances of a service. This load balancing enhances the scalability and reliability of applications.

Let’s dive into some practical exercises to understand the Discovery Service, Microservices, and Gateway applications.

3. Create a Spring Eureka Server application

Creating a Spring Eureka Server application is an essential step in building resilient and scalable microservices architectures. In this process, you’ll harness the power of Spring to develop a central service registry capable of managing service registration, discovery, and load balancing.

3.1 Incorporating Dependencies into pom.xml

Establish a fresh Spring Boot project using Spring Initialzr or leverage an existing one. Include the necessary dependencies (such as eureka-service, etc.) and dependency management in your project’s pom.xml file.

pom.xml

<dependencies>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-netflix-eureka-server</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>

3.2 Configure Application Properties

Let us write the application.properties and understand the important properties.

  • server.port: The value assigned to the port means the application will listen on port 8761
  • eureka.client.register-with-eureka: This property configures whether the microservice should register itself with the Eureka Server. When set to false, it means that the microservice will not register itself as an available service with Eureka. In other words, the Eureka Server will not be aware of the existence of this particular microservice.
  • eureka.client.fetch-registry: This property configures whether the microservice should fetch and cache the registry of available services from the Eureka Server. When set to false, the microservice will not actively retrieve and maintain a local copy of the service registry.

application.properties

# spring properties
server.port=8761
spring.application.name=eureka-server-registry

# This property configures whether the microservice should register itself with the Eureka Server. When set to false, it means that the microservice will not register itself as an available service with Eureka. In other words, the Eureka Server will not be aware of the existence of this particular microservice.
eureka.client.register-with-eureka=false

# This property configures whether the microservice should fetch and cache the registry of available services from the Eureka Server. When set to false, the microservice will not actively retrieve and maintain a local copy of the service registry.
eureka.client.fetch-registry=false

3.3 Create a Main Class

Let’s create the EurekaserverApplication class which will act as the bootstrapper / launcher for the Eureka Server Spring Boot application.

  • @SpringBootApplication: This annotation is used to mark the class as a Spring Boot application. It combines three commonly used annotations: @Configuration, @EnableAutoConfiguration, and @ComponentScan. It tells Spring Boot to configure the application, enable auto-configuration based on the classpath, and scan for components (e.g., beans, controllers, etc.) in the package where the application class is located.
  • @EnableEurekaServer: This annotation is specific to Spring Cloud Netflix Eureka, a service discovery and registry server. When applied to a Spring Boot application, it configures the application to act as an Eureka Server, which allows it to maintain a registry of services (microservices) that are registered with it and provides service discovery capabilities to other microservices.
  • public static void main(String[] args): This is the main method of the class. It serves as the entry point for the application. When you execute the main method, it starts the Spring Boot application.
  • SpringApplication.run(EurekaserverApplication.class, args): This line of code starts the Spring Boot application. It takes two arguments: the main application class (EurekaserverApplication.class) and the command-line arguments (args). It initializes the Spring application context, loads the application’s configuration, and starts the embedded web server (if needed).

EurekaserverApplication.java

package com.eurekaserver.eurekaserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaserverApplication {

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

In summary, this class sets up a Spring Boot application with the @EnableEurekaServer annotation, making it function as an Eureka Server, which can be used for service registration and discovery in a microservices architecture. When you run this application, it starts the Eureka Server and provides a web-based dashboard where you can monitor and manage registered services.

3.4 Run the Application

Run the application. By default, the server will listen on port number – 8761. So once the application is started navigate to the url to view the Discovery Service dashboard. The dashboard will show that no application is registered with it yet.

Fig. 1: Service Discovery Dashboard

4. Create a Spring Eureka Client application

Creating a Spring Eureka Client application involves configuring a microservice to register itself with an Eureka Server and taking advantage of service discovery capabilities. This pivotal step in building microservices architectures ensures that services can dynamically discover and communicate with each other within a distributed system. In this context, the Spring Eureka Client application plays a crucial role in enhancing the scalability, resilience, and flexibility of cloud-native applications by seamlessly integrating with a service registry like Eureka.

4.1 Incorporating Dependencies into pom.xml

Establish a fresh Spring Boot project using Spring Initialzr or leverage an existing one. Include the necessary dependencies (such as eureka-client, etc.) and dependency management in your project’s pom.xml file.

pom.xml

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<!-- adding eureka client dependency -->
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-netflix-eureka-client</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>

4.2 Configure Application Properties

Let us write the application.properties and understand the important properties.

  • server.port: The value assigned to the port means the application will listen on port 9300
  • eureka.instance.client.serviceUrl.defaultZone: This property specifies the URL of the Eureka Server that the microservice should connect to. It is used to define the default zone, which is essentially the location of the Eureka Server where service registration and discovery occur. When a microservice starts up and wants to register itself with Eureka or discover other services, it needs to know the address of the Eureka Server. This property is usually set to the URL of the Eureka Server.

application.properties

# spring properties
server.port=9300
spring.application.name=USER-SERVICE

# This property specifies the URL of the Eureka Server that the microservice should connect to. It is used to define the default zone, which is essentially the location of the Eureka Server where service registration and discovery occur.
# When a microservice starts up and wants to register itself with Eureka or discover other services, it needs to know the address of the Eureka Server. This property is usually set to the URL of the Eureka Server.
eureka.instance.client.serviceUrl.defaultZone=http://localhost:8761/eureka/

4.3 Create a Controller Class

  • @RestController: This annotation marks the class as a Spring MVC controller that handles incoming HTTP requests. It indicates that the class’s methods will return data that should be directly serialized into the HTTP response body, making it suitable for building RESTful APIs.
  • @RequestMapping("/user"): This annotation at the class level specifies the base URL path (“/user”) that this controller will handle. Any HTTP request starting with this path will be directed to methods within this controller.
  • @GetMapping("/welcome"): This annotation is applied to the “welcome” method, specifying that it will handle HTTP GET requests to the endpoint “/user/welcome.” It maps this method to a specific URL route.
  • public ResponseEntity<String> welcome() { ... }: This is a Java method that handles the incoming HTTP GET request. It returns a ResponseEntity object containing a String. The ResponseEntity allows you to control the HTTP response, including the status code and headers.
  • return ResponseEntity.of(Optional.of("Welcome to user service.")): In this line, a ResponseEntity is created and initialized with the message “Welcome to user service.” wrapped in an Optional. The response will contain this message, and the HTTP status will be set to 200 (OK) by default.

ApiController.java

package com.eurekaclient.eurekaclientuserservice.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Optional;

@RestController
@RequestMapping("/user")
public class ApiController {

    // HTTP GET - http://localhost:9300/api/welcome
    @GetMapping("/welcome")
    public ResponseEntity<String> welcome() {
        return ResponseEntity.of(Optional.of("Welcome to user service."));
    }
}

In summary, the ApiController class defines a RESTful controller in a Spring-based application. It handles GET requests to the /user/welcome endpoint, responding with a Welcome to user service. message as an HTTP response. The @RestController and @RequestMapping annotations define its behavior, and the @GetMapping annotation specifies the route for the welcome method.

4.4 Create a Main Class

The provided Java class EurekaclientUserserviceApplication is the entry point for the Eureka Client Spring Boot application and will help us bootstrap and launch it.

  • @SpringBootApplication: This annotation marks the class as a Spring Boot application and combines @Configuration, @EnableAutoConfiguration, and @ComponentScan annotations. It serves as the entry point for the Spring Boot application, enabling auto-configuration and component scanning.
  • @EnableDiscoveryClient: Specific to Spring Cloud, this annotation configures the microservice to act as a discovery client, interacting with a service registry like Eureka. It enables registration with the service registry and facilitates the discovery and communication with other registered services.
  • public static void main(String[] args): This method is the application’s main entry point, executed when the application is launched. It initiates the Spring Boot application.
  • SpringApplication.run(EurekaclientUserserviceApplication.class, args): This line starts the Spring Boot application by initializing the Spring context and loading the configuration, using EurekaclientUserserviceApplication.class as the main class and args for any command-line arguments.

EurekaclientUserserviceApplication.java

package com.eurekaclient.eurekaclientuserservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class EurekaclientUserserviceApplication {

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

4.5 Run the Application

Run the application. By default, the client application server will listen on port number – 9300. Once the application is started navigate to the url to view that the application is registered on the Discovery Service dashboard with the name USER-SERVICE.

Fig. 2: User Service on Service Discovery Dashboard

The Eureka client application will also expose the following endpoint – http://localhost:9300/api/welcome that will be registered at the Eureka server and will be consumed via the Gateway application. Do note the url can still be accessed individually via the port number – 9300.

Curl request

-- HTTP GET: Show a welcome message for the user
http://localhost:9300/api/welcome

5. Create a Gateway Application

Creating a Gateway Application is a fundamental aspect of building modern microservices architectures and APIs. A gateway serves as a central entry point that manages incoming requests, directs them to the appropriate services, and applies various cross-cutting concerns such as authentication, load balancing, and routing. In essence, it acts as a protective shield for your microservices, improving security, scalability, and maintainability.

5.1 Incorporating Dependencies into pom.xml

Establish a fresh Spring Boot project using Spring Initialzr or leverage an existing one. Include the necessary dependencies (such as eureka-client, spring-cloud-starter-gateway, etc.) and dependency management in your project’s pom.xml file.

pom.xml

<dependencies>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-gateway</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-netflix-eureka-client</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>

5.2 Create a Main Class

Just like the “main” classes of the Eureka Server and Client applications mentioned earlier, the provided  GatewayApplication class is the Gateway’s Spring Boot application bootstrapper and launcher.

  • @Bean: This annotation designates the method as a producer of a Spring Bean, which is a reusable component managed by the Spring framework. In this context, the method creates a RouteLocator bean responsible for defining routing rules.
  • RouteLocator: This is an interface provided by Spring Cloud Gateway representing a collection of routes. Each route defines how incoming HTTP requests should be handled and where they should be forwarded.
  • routeLocator(RouteLocatorBuilder locatorBuilder): This method accepts a RouteLocatorBuilder as a parameter. The RouteLocatorBuilder is a utility class in Spring Cloud Gateway, simplifying the creation of routing rules.
  • return locatorBuilder.routes().route("user-service-route", route -> route.path("/user/**").uri("lb://USER-SERVICE")): Here, we use the locatorBuilder to begin defining routing rules. It specifies that a new set of route definitions is being created. Each destination starts with the lb followed by the microservice name. When the gateway application uses the discovery server to resolve the microservice name into an IP address of a microservice instance and establish a connection to it. Furthermore, lb means load balancer, indicating that the gateway will distribute the request if the discovery service returns several instances of the same microservice.
  • .build(): This method finalizes the route configuration and constructs the RouteLocator bean.

GatewayApplication.java

package com.example.gatewayapp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@EnableDiscoveryClient
public class GatewayappApplication {

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

    @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder locatorBuilder) {
        return locatorBuilder
                .routes()
                .route("user-service-route", route -> route.path("/user/**").uri("lb://USER-SERVICE"))  // http://localhost:9800/user/welcome
                .build();
    }
}

In summary, this method configures a route in Spring Cloud Gateway named “user-service-route.” It instructs the application to forward incoming requests with paths starting with “/user/” to a service named “USER-SERVICE” using load balancing. The lb:// prefix indicates that load balancing should be employed, typically through a service registry like Eureka, to determine the precise service instance to which each request should be dispatched. This routing configuration is particularly valuable in a microservices architecture, where “USER-SERVICE” is one of the registered services, and the Gateway serves as the front end to route requests based on the URL path.

5.3 Run the Application

Run the application. The gateway application will be started on the port number – 9800 and will also be registered on the Discovery Service dashboard with the name GATEWAY-APPLICATION. Once the application is registered successfully hit the below endpoint to communicate with the USER-SERVICE.

Fig. 3: Gateway application on Service Discovery Dashboard

Gateway Endpoint for User Service

http://localhost:9800/user/welcome

Once this endpoint is triggered in the browser or Postman tool the gateway will successfully route the request to the USER-SERVICE via the Eureka server. The gateway on receiving such a destination url will use the discovery server to resolve the microservice name to an ip address and port number. This request is further passed on to the destination service and the response is returned to the user via the gateway application. Since USER-SERVICE is running on port number 9300 the request is internally forwarded to the correct ip address and port number.

Fig.4: Output from User Service via Gateway Application

6. Conclusion

In summary, the Discovery Server, Eureka Client, and Gateway Application are indispensable components within the realm of microservices and cloud-native architectures. They collectively contribute to the efficiency, scalability, and robustness of distributed systems. The Discovery Server functions as the backbone, offering a centralized repository for service registration and discovery, thereby simplifying the complex task of managing a dynamic network of services. The Eureka Client assumes the role of a service registry user, registering itself with the Discovery Server and harnessing its service discovery capabilities to enable effortless communication between microservices. On the other hand, Gateway Applications serve as the system’s front door, providing a secure and unified interface for clients. These gateways efficiently handle routing, load balancing, and security enforcement, enhancing the overall reliability and performance of the microservices ecosystem.

7. Download the Project

This was a tutorial on Using Spring Cloud Gateway and Discovery Service for Seamless Request Routing.

Download
You can download the full source code of this example here: Simplified Routing via Spring Cloud

Yatin

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button