Tutoriel: Architecture de Spring Cloud
Introduction
Spring Cloud est une solution robuste pour créer des systèmes distribués et des microservices. Il aide à gérer la complexité des architectures distribuées avec des composants clés comme Eureka, Config Server, Zuul, et Hystrix.
Cloud Computing Architecture for Spring Cloud
Cloud Computing Architecture for Spring Cloud detailed
Cloud Computing Architecture for Azure Services
Spring Boot Microservices (Service A, B, and C):
- Role: These are the core business services that provide functionality like fetching data, processing requests, etc.
- Input: Requests from clients or other services routed via Zuul.
- Output: Processed data or service responses sent back to the requesting client or service.
Eureka Server:
- Role: Acts as a service registry where all services (Service A, B, and C) register themselves. It allows services to discover each other dynamically.
- Input: Registration requests from services and discovery requests from other services (or Zuul).
- Output: Service instances' metadata and location information.
Config Server:
- Role: Provides a centralized external configuration for all services (Service A, B, C). Configurations are typically stored in a Git repository.
- Input: Configuration requests from services (Service A, B, C) when they start or refresh.
- Output: Configuration properties such as URLs, database credentials, or feature toggles.
Zuul Gateway:
- Role: Acts as an API Gateway that routes external requests to the correct microservice (Service A, B, or C). It provides a unified entry point for all external requests.
- Input: HTTP requests from clients.
- Output: Routed responses from appropriate microservices back to the client.
Hystrix:
- Role: Provides fault tolerance and latency tolerance for distributed systems. It wraps service calls in a circuit breaker pattern, so if one service is down or slow, the system continues to function by either providing fallback responses or short-circuiting the failed request.
- Input: Service calls (via Feign clients) made from one service to another.
- Output: Either the normal service response or a fallback response if the service is down.
Eureka
Eureka is a service registry that allows microservices to register themselves so they can be discovered by others.
- Service Registration and Discovery
- Instance management in dynamic cloud environments
- Supports high availability through failover mechanisms
- Rest-based client-server interactions
Exemple
Étapes pour configurer un projet Spring Cloud
Voici un guide étape par étape pour configurer une architecture de microservices avec Spring Cloud.
Étape 1: Créer un projet Spring Boot
- Accédez à https://start.spring.io/
- Sélectionnez les dépendances Spring Web et Spring Cloud Dependencies
- Générez le projet et importez-le dans votre IDE préféré (IntelliJ, Eclipse, etc.)
Étape 2: Eureka Server Setup
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.9</version> <!-- Downgraded Spring Boot to a compatible version -->
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.emsieureka</groupId>
<artifactId>eurekaserver</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eurekaserver</name>
<description>Demo project for Spring Boot Eureka Server</description>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2022.0.1</spring-cloud.version> <!-- Spring Cloud 2022.x release train -->
</properties>
<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>
<dependencies>
<!-- Spring Boot Web Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Eureka Server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- Spring Boot DevTools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- Spring Boot Test Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Main Class (EurekaServerApplication.java)
package com.emsieureka.eurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication // Indique que c'est une application Spring Boot, activant l'auto-configuration et le scan des composants
@EnableEurekaServer // Active cette application en tant que serveur Eureka, permettant la découverte de services
public class EurekaServerApplication { // Classe principale de l'application serveur Eureka
public static void main(String[] args) { // Point d'entrée de l'application Java
SpringApplication.run(EurekaServerApplication.class, args); // Lance l'application Spring
}
}
application.properties
spring.application.name=eurekaserver
server.port=8761
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
2. Service A: Greeting Service
il faut créer un projet spring Boot appler Service1pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.emsi</groupId>
<artifactId>serviceA</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>serviceA</name>
<description>Demo project for Spring Boot</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2023.0.3</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version> <!-- Use property for version -->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<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.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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Main Class (ServiceAApplication.java)
package com.emsi.serviceA;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication // Indique que c'est une application Spring Boot, activant l'auto-configuration et le scan des composants
@EnableDiscoveryClient
public class ServiceAApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceAApplication.class, args);
}
}
@RestController
class GreetingController {
@GetMapping("/greeting")
public String getGreeting() {
return "Hello from Service A!";
}
}
application.properties
spring.application.name=serviceA
server.port=8081
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
3. Service B: Date Service
il faut créer un projet spring Boot appeler Service2pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.emsi</groupId>
<artifactId>serviceB</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>serviceB</name>
<description>Demo project for Spring Boot</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2023.0.3</spring-cloud.version>
</properties>
<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.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>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Main Class (ServiceBApplication.java)
package com.emsi.serviceB;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDate;
@SpringBootApplication // Indique que c'est une application Spring Boot, activant l'auto-configuration et le scan des composants
@EnableDiscoveryClient
public class ServiceBApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceBApplication.class, args);
}
}
@RestController
class DateController {
@GetMapping("/date")
public String getCurrentDate() {
return "Current Date: " + LocalDate.now();
}
}
application.properties
server.port=8082
spring.application.name=service-b
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
4. Service C: Aggregator Service
il faut créer un projet spring Boot appeler Service3pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.emsi</groupId>
<artifactId>serviceC</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>serviceC</name>
<description>Demo project for Spring Boot</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2023.0.3</spring-cloud.version>
</properties>
<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-openfeign</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>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Main Class (ServiceCApplication.java)
package com.emsi.serviceC;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
@SpringBootApplication // Indique que c'est une application Spring Boot, activant l'auto-configuration et le scan des composants
@EnableDiscoveryClient
@EnableFeignClients // Active l'utilisation des clients Feign pour effectuer des requêtes HTTP vers d'autres services
public class ServiceCApplication { // Classe principale de l'application
public static void main(String[] args) { // Point d'entrée de l'application Java
SpringApplication.run(ServiceCApplication.class, args); // Lance l'application Spring
}
}
@RestController // Indique que cette classe est un contrôleur Spring MVC avec des capacités RESTful
class AggregatorController { // Contrôleur pour gérer les requêtes entrantes et agréger les données
@Autowired // Injecte le bean GreetingClient
private GreetingClient greetingClient; // Client Feign pour appeler le Service A
@Autowired // Injecte le bean DateClient
private DateClient dateClient; // Client Feign pour appeler le Service B
@GetMapping("/aggregate") // Mappe les requêtes HTTP GET vers cette méthode à l'endpoint "/aggregate"
public String getAggregateData() { // Méthode pour agréger les données des deux services
String greeting = greetingClient.getGreeting(); // Appelle le Service A pour obtenir le message de salutation
String date = dateClient.getCurrentDate(); // Appelle le Service B pour obtenir la date actuelle
return greeting + " | " + date; // Retourne la réponse agrégée
}
}
@FeignClient(name = "service-a") // Déclare un client Feign pour le Service A avec le nom "service-a"
interface GreetingClient {
@GetMapping("/greeting") // Mappe la requête HTTP GET vers l'endpoint "/greeting" du Service A
String getGreeting(); // Méthode pour obtenir le message de salutation
}
@FeignClient(name = "service-b") // Déclare un client Feign pour le Service B avec le nom "service-b"
interface DateClient {
@GetMapping("/date") // Mappe la requête HTTP GET vers l'endpoint "/date" du Service B
String getCurrentDate(); // Méthode pour obtenir la date actuelle
}
application.properties
server.port=8083
spring.application.name=service-c
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
Run the Application
Start Eureka Server:
Run the Eureka Server application (EurekaServerApplication).
It will start on port 8761 and host the service registry.
Start Service A, Service B, and Service C:
Run each service on their respective ports (8081, 8082, 8083).
These services will automatically register themselves with the Eureka Server.
Test the Setup:
Access the Eureka Server dashboard at http://localhost:8761 to see the registered instances (service-a, service-b, and service-c).
Visit http://localhost:8083/aggregate to trigger Service C's aggregation. This will call Service A for a greeting and Service B for the current date and combine the results.