Spring Cloud Consul
- Spring Cloud Consul integrates Consul with Spring Boot applications, facilitating service discovery, configuration management, and distributed system coordination.
- Leverages the open-source Consul platform for managing complex microservice architectures, enhancing service resilience and ease of scaling.
Install and Start Consul
- Download Consul: https://developer.hashicorp.com/consul/install
- Unzip the downloaded file.
- add the location to your PATH for Windows).
- consul agent -dev
- it will run on localhost:8500.
- Access the Consul UI: http://localhost:8500 to access the Consul UI.
Tp1: Service Discovery
1. Client-A Application
Step 2.1: Create a Spring Boot Project for Client-ADependencies:
- Spring Web
- Spring Cloud Starter Consul Discovery
- Spring Boot DevTools (optional for easier development)
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
</dependencies>
Step 2.2: Configure application.yml for Client-A
Create a file named application.yml in the src/main/resources:
spring:
application:
name: client-a
cloud:
consul:
host: localhost
port: 8500
Step 2.3: Create a REST Controller for Client-A
Create a REST controller that exposes an endpoint:
package com.example.clienta;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ClientAController {
@GetMapping("/greet")
public String greet() {
return "Hello from Client A!";
}
}
Step 2.4: Create the Main Application Class
package com.example.clienta;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ClientAApplication {
public static void main(String[] args) {
SpringApplication.run(ClientAApplication.class, args);
}
}
Run Client A
2. Client-B Application Setup
Create a Spring Boot Project for Client-B
Similar to Client-A, create another Spring Boot project for Client-B with the same dependencies.pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
</dependencies>
application.yml for Client-B
Create a file named application.yml in the src/main/resources directory with the following content:
spring:
application:
name: client-b
cloud:
consul:
host: localhost
port: 8500
Create a REST Controller for Client-B
Create a REST controller that calls Client-A:
package com.example.clientb;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class ClientBController {
@Autowired
private DiscoveryClient discoveryClient;
private final RestTemplate restTemplate = new RestTemplate();
@GetMapping("/call-a")
public String callClientA() {
List<ServiceInstance> instances = discoveryClient.getInstances("client-a");
if (instances != null && !instances.isEmpty()) {
String clientAUrl = instances.get(0).getUri().toString() + "/greet";
return restTemplate.getForObject(clientAUrl, String.class);
}
return "Client A not available";
}
}
- DiscoveryClient: Used for service discovery. It provides information about registered services (such as their URLs) and allows applications to locate other services by name.
- RestTemplate: Used for making HTTP requests. After obtaining the service URL from DiscoveryClient, RestTemplate uses this URL to make RESTful calls to the discovered service.
- Client-B uses DiscoveryClient to retrieve the URL of Client-A from the Consul Server.
- DiscoveryClientcontacts Consul Server to fetch the URL of Client-A.
- RestTemplate then uses the retrieved URL to make HTTP requests to Client-A.
Create the Main Application Class for Client-B
package com.example.clientb;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ClientBApplication {
public static void main(String[] args) {
SpringApplication.run(ClientBApplication.class, args);
}
}
Run
- Run Consul
- Run Client-A:
- Run Client-B:
Test the API: Use a tool like Postman or cURL to test the endpoints.
Call Client-A: http://localhost:8080/greet
Call Client-B :that call A :http://localhost:8081/call-a
You should see:
The response from Client-A when hitting the /greet endpoint.
The response from Client-B calling Client-A through service discovery.
Configuration Management
Consul: Acts as a centralized configuration storage where configurations (key-value pairs) for applications are stored.
Spring Boot Application: The application loads configurations from Consul when it starts and injects them into the
@ConfigurationProperties
class (e.g.,AppConfig
).@RefreshScope: This annotation allows specific beans, such as
AppConfig
, to be refreshed with updated values when a configuration refresh is triggered.Actuator
/refresh
Endpoint: When a POST request is sent to/actuator/refresh
, Spring Cloud reloads the configurations from Consul without restarting the application.User/Admin: Can update configuration values directly in Consul and then trigger the
/refresh
endpoint, making the updated configurations immediately available in the application.
Create Configuration
Create Configuration in Consul Key-Value Store- Open the Consul UI at http://localhost:8500.
- Navigate to Key/Value.
- Create a key-value pair :config/application-name/data
- config/client-a/messge/
- use cmd :consul kv put config/client-a/message "client a message"
Create the Spring Boot Application
including the following dependencies:- Spring Web (to create REST endpoints)
- Spring Cloud Starter Consul Config (for Consul integration)
- Spring Boot Actuator (for monitoring and refreshing)
pom.xml
:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Configure application.yml for Consul
spring:
application:
name: clienta
cloud:
consul:
host: localhost
port: 8500
discovery:
enabled: true
config:
enabled: true
prefix: config
config:
import: "consul:"
management:
endpoints:
web:
exposure:
include: health,info,refresh
server:
port: 8080
Use Consul Configuration in the Application
package com.example.clienta;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@RefreshScope
@Component
public class AppConfig {
@Value("${message}")
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Create a REST Controller to Display the Configuration
package com.example.clienta;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConfigController {
private final AppConfig appConfig;
public ConfigController(AppConfig appConfig) {
this.appConfig = appConfig;
}
@GetMapping("/message")
public String getMessage() {
return appConfig.getMessage();
}
}
Run
Enable Actuator’s Refresh Endpoint In application.yml, enable the /actuator/refresh endpoint by adding the following configuration:
management:
endpoints:
web:
exposure:
include: refresh
Run
http://localhost:8080/message You should see the value of message as set in Consul (Hello from Consul Config!).Update the Configuration in Consul: Go to http://localhost:8500. Update the message key under config/client-a/data to a new value, like "Hello, updated from Consul!".
Refresh the Configuration: you can
- 1.Restart the application
-
2.send a POST request to the /actuator/refresh endpoint:
http://localhost:8080/messagePOST http://localhost:8080/actuator/refresh
Load balancing
- Load balancing is a technique for distributing network traffic across a server farm.
- It optimizes network performance, reliability, and capacity.
- Reduces latency by evenly distributing demand among multiple servers and resources.
- Uses an appliance (physical or virtual) to identify the best server for client requests in real-time.
- Prevents overwhelming a single server during heavy network traffic.
- Provides failover; if one server fails, workloads are redirected to a backup server.
Client Request:
- The process starts with a client making a request.
Load Balancer:
- The load balancer receives the client request and determines which server is available to handle the request.
Available Servers:
- The load balancer evaluates the pool of available servers (e.g., Server A, Server B, Server C) and chooses one to forward the request.
Forwarding Requests:
- The load balancer forwards the request to the selected server, which processes the request and returns a response.
Response:
- The server processes the request and sends the response back to the client.
Failover Handling:
- If the load balancer detects a failure while processing the request (for example, if the selected server becomes unavailable):
- The load balancer redirects the request to a backup server.
- The backup server processes the request and returns the response to the client.
- If the load balancer detects a failure while processing the request (for example, if the selected server becomes unavailable):