Spring Cloud Gateway

Introduction

Qu'est-ce qu'une API Gateway ?

Imaginez une API Gateway comme un réceptionniste dans un grand hôtel :

  • Les clients (applications) arrivent à la réception (Gateway)
  • Le réceptionniste vérifie qui ils sont (authentification)
  • Il les dirige vers le bon service (routage)
  • Il peut traduire les messages (transformation)
  • Il surveille qui entre et sort (monitoring)

Exemple concret :

Sans Gateway :

Client → Service A (http://localhost:8081)
Client → Service B (http://localhost:8082)
Client → Service C (http://localhost:8083)

Avec Gateway :

Client → Gateway (http://localhost:8080)
        → Service A (/api/service-a/** → http://localhost:8081)
        → Service B (/api/service-b/** → http://localhost:8082)
        → Service C (/api/service-c/** → http://localhost:8083)

Qu'est-ce que Spring Cloud Gateway ?

Spring Cloud Gateway est une implémentation moderne et réactive d'API Gateway basée sur Project Reactor (programmation réactive non-bloquante).

Avantages de la programmation réactive :

  • Performance : Gère plus de requêtes avec moins de ressources
  • Scalabilité : S'adapte automatiquement à la charge
  • Résilience : Gère mieux les erreurs et les timeouts

Architecture complète

graph TD A[Client Externe] --> B[Spring Cloud Gateway] B --> C[Eureka Server - Service Discovery] C --> D[Registre des Services] D --> E[Service A - Port 8081] D --> F[Service B - Port 8082] D --> G[Service C - Port 8083] B --> E B --> F B --> G H[Filtres - Sécurité/Auth] --> B I[Rate Limiting] --> B J[Monitoring] --> B style B fill:#4CAF50,stroke:#388E3C style C fill:#2196F3,stroke:#0D47A1 style D fill:#2196F3,stroke:#0D47A1 style E fill:#FF9800,stroke:#E65100 style F fill:#FF9800,stroke:#E65100 style G fill:#FF9800,stroke:#E65100 style H fill:#9C27B0,stroke:#4A148C style I fill:#FF9800,stroke:#E65100 style J fill:#F44336,stroke:#B71C1C

Flux de fonctionnement :

  1. Enregistrement : Services A, B, C s'enregistrent auprès d'Eureka
  2. Découverte : Gateway récupère la liste des services depuis Eureka
  3. Routage : Gateway redirige les requêtes vers les bons services
  4. Filtrage : Gateway applique sécurité, rate limiting, etc.
  5. Monitoring : Toutes les opérations sont surveillées

Configuration deja fait dans le tp de eureka :

Configuration attendue :

  • Eureka Server : Port 8761
  • Service A : Port 8081, nom: service-a
  • Service B : Port 8082, nom: service-b
  • Service C : Port 8083, nom: service-c

Partie 1 : Créer le Spring Cloud Gateway

1 Créer le projet Spring Cloud Gateway via Spring Initializr

Étape 1.1 : Accéder à Spring Initializr

Rendez-vous sur https://start.spring.io

Étape 1.2 : Configurer le projet

Paramètres à sélectionner :

  • Project : Maven Project (outil de build)
  • Language : Java (langage de programmation)
  • Spring Boot : 2.7.x ou supérieur (version du framework)
  • Project Metadata :
    • Group : com.example
    • Artifact : api-gateway
    • Name : api-gateway
    • Description : Spring Cloud Gateway -reactive
    • Package name : com.example.apigateway
    • Packaging : Jar
    • Java : 11

Étape 1.3 : Sélectionner les dépendances

Dépendances obligatoires :

  • Spring Cloud Gateway -reactive : Cœur du Gateway réactif
  • Eureka Discovery Client : Pour communiquer avec Eureka

Dépendances optionnelles (recommandées) :

  • Spring Boot Actuator : Pour le monitoring
  • Spring Boot Security : Pour la sécurité

Étape 1.4 : Générer et importer le projet

  1. Cliquez sur "Generate" pour télécharger le projet ZIP
  2. Décompressez le fichier
  3. Ouvrez le projet dans votre IDE
  4. Attendez que Maven télécharge toutes les dépendances

2 Comprendre et configurer le fichier pom.xml

Structure du fichier pom.xml - Explication ligne par ligne :


    <!-- Dépendances du projet -->
    <dependencies>
        <!-- Dépendance principale pour Spring Cloud Gateway -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <!-- Pas de version = héritée de spring-cloud-dependencies -->
        </dependency>
        
        <!-- Client Eureka pour discovery -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        
        <!-- Pour le monitoring et la santé de l'application -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
    

Comment Maven gère les dépendances :

  1. Lecture du pom.xml : Maven lit votre configuration
  2. Téléchargement : Il télécharge automatiquement les bibliothèques depuis des dépôts comme Maven Central
  3. Résolution : Il résout les dépendances transitives (dépendances des dépendances)
  4. Compilation : Il compile votre code avec toutes les bibliothèques

3 Configuration du Gateway avec application.properties - Explication détaillée

Créer le fichier application.properties :

Chemin : src/main/resources/application.properties

Ce fichier contient toute la configuration de votre application. Il est lu automatiquement au démarrage par Spring Boot.

Configuration ligne par ligne avec explications :

############################################
# 🌐 CONFIGURATION DE BASE DU SERVEUR
############################################

# Port du Gateway - c’est sur ce port que les clients externes enverront leurs requêtes.
server.port=8080

# Nom de l’application - utilisé pour l’enregistrement auprès d’Eureka.
# Chaque service doit avoir un nom unique pour être distingué dans le registre.
spring.application.name=api-gateway


############################################
# 🧭 CONFIGURATION DU CLIENT EUREKA
############################################

# URL du serveur Eureka - indique où se trouve le service de découverte.
# Le format standard est : http://:/eureka/
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

# Enregistrement du Gateway dans le serveur Eureka
# true : le Gateway s’inscrit lui-même dans le registre.
eureka.client.register-with-eureka=true

# Récupération du registre des services
# true : permet au Gateway de connaître tous les services enregistrés (pour le routage dynamique).
eureka.client.fetch-registry=true

# Utilisation de l’adresse IP plutôt que du nom d’hôte.
# Recommandé dans les environnements Docker ou Cloud.
eureka.instance.prefer-ip-address=true


############################################
# 🚦 CONFIGURATION DU ROUTAGE AUTOMATIQUE
############################################

# Active la découverte automatique des routes depuis Eureka.
# Si activé, chaque service enregistré dans Eureka devient accessible via une route.
spring.cloud.gateway.discovery.locator.enabled=true

# Convertit automatiquement les noms de services en minuscules.
# Exemple : SERVICE-A → service-a (plus cohérent pour les URLs).
spring.cloud.gateway.discovery.locator.lower-case-service-id=true


############################################
# 🧩 CONFIGURATION DES ROUTES MANUELLES
############################################

# === Route personnalisée vers le microservice Service A ===
spring.cloud.gateway.routes[0].id=service-a
spring.cloud.gateway.routes[0].uri=lb://service-a
spring.cloud.gateway.routes[0].predicates[0]=Path=/api/service-a/**
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=2
# 🔹 "lb://" active le load-balancing basé sur Eureka.
# 🔹 "Path" définit le motif d’URL correspondant à cette route.
# 🔹 "StripPrefix=2" supprime les deux premiers segments du chemin (/api/service-a/ → /).

# === Route personnalisée vers le microservice Service B ===
spring.cloud.gateway.routes[1].id=service-b
spring.cloud.gateway.routes[1].uri=lb://service-b
spring.cloud.gateway.routes[1].predicates[0]=Path=/api/service-b/**
spring.cloud.gateway.routes[1].filters[0]=StripPrefix=2

# === Route personnalisée vers le microservice Service C ===
spring.cloud.gateway.routes[2].id=service-c
spring.cloud.gateway.routes[2].uri=lb://service-c
spring.cloud.gateway.routes[2].predicates[0]=Path=/api/service-c/**
spring.cloud.gateway.routes[2].filters[0]=StripPrefix=2


############################################
# 🧱 CONFIGURATION DES FILTRES GLOBAUX
############################################

# Évite les doublons dans les en-têtes de réponse (CORS notamment).
# DedupeResponseHeader fusionne les en-têtes dupliqués pour plus de propreté.
spring.cloud.gateway.default-filters[0]=DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin


############################################
# 📊 CONFIGURATION ACTUATOR (MONITORING)
############################################

# Expose les endpoints pour la supervision et le diagnostic.
# - gateway : affiche les routes configurées
# - health : état de santé du Gateway
# - info : informations générales de l’application
# - metrics : indicateurs de performance
management.endpoints.web.exposure.include=gateway,health,info,metrics


############################################
# 🪵 CONFIGURATION DE LOGGING
############################################

# Niveau de logs pour le moteur Spring Cloud Gateway.
# DEBUG : niveau verbeux utile pour le développement et le débogage.
logging.level.org.springframework.cloud.gateway=DEBUG

# Logs détaillés pour le client HTTP réactif (basé sur Reactor Netty).
logging.level.reactor.netty.http.client=DEBUG


############################################
# ⏱️ CONFIGURATION DES TIMEOUTS
############################################

# Temps maximal d’attente lors de la connexion à un service cible (en millisecondes).
spring.cloud.gateway.httpclient.connect-timeout=5000

# Temps maximal d’attente pour la réponse complète d’un service (en secondes).
spring.cloud.gateway.httpclient.response-timeout=10s


############################################
# 🧩 CONFIGURATION DE L’INSTANCE EUREKA
############################################

# Identifiant unique de cette instance - utile pour le suivi et le débogage.
# Exemple : api-gateway:8080
eureka.instance.instance-id=${spring.application.name}:${server.port}

# Fréquence à laquelle le Gateway renouvelle son "bail" auprès d’Eureka (en secondes).
# Un renouvellement est une sorte de "ping" qui indique que le service est toujours actif.
eureka.instance.lease-renewal-interval-in-seconds=30

# Délai maximal avant qu’un service soit marqué comme inactif si aucun renouvellement n’est reçu.
eureka.instance.lease-expiration-duration-in-seconds=90

Comprendre les concepts clés :

lb://service-name :

  • lb = load balancing
  • service-name = nom logique du service dans Eureka
  • Gateway utilisera Eureka pour trouver l'adresse réelle

StripPrefix=2 :

  • Supprime les 2 premiers segments du chemin
  • Exemple : /api/service-a/health devient /health
  • Utile pour cacher la structure interne aux clients

Predicates :

  • Conditions qui déterminent si une route doit être utilisée
  • Path=/api/service-a/** = tous les chemins commençant par /api/service-a/
  • Autres exemples : Method=GET, Header=X-API-Key, etc.

4 Configuration Java alternative - Explication détaillée

Quand utiliser la configuration Java ?

La configuration Java est plus puissante et flexible que les fichiers .properties, surtout pour des configurations complexes ou dynamiques.

Créer la classe GatewayConfig.java :

Chemin : src/main/java/com/example/apigateway/config/GatewayConfig.java

// Annotation pour dire que c'est une classe de configuration Spring
@Configuration
public class GatewayConfig {
    
    // Annotation @Bean pour créer un composant Spring géré
    @Bean
    // RouteLocator = objet qui définit toutes les routes du Gateway
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        // builder.routes() = commence à construire les routes
        return builder.routes()
            
            // === ROUTE POUR SERVICE A ===
            // .route("service-a", r -> r...) = crée une route nommée "service-a"
            .route("service-a", r -> r
                // .path("/api/service-a/**") = condition : chemin commence par /api/service-a/
                .path("/api/service-a/**")
                
                // .filters(f -> f...) = applique des filtres à cette route
                .filters(f -> f
                    // .stripPrefix(2) = retire les 2 premiers segments du chemin
                    .stripPrefix(2)
                    // .addResponseHeader("X-Response-Service", "Service-A") = ajoute un header à la réponse
                    .addResponseHeader("X-Response-Service", "Service-A")
                    // .retry(3) = réessaie 3 fois en cas d'erreur
                    .retry(3)
                )
                // .uri("lb://service-a") = destination avec load balancing vers service-a
                .uri("lb://service-a"))
            
            // === ROUTE POUR SERVICE B ===
            .route("service-b", r -> r
                .path("/api/service-b/**")
                .filters(f -> f
                    .stripPrefix(2)
                    .addResponseHeader("X-Response-Service", "Service-B")
                    .retry(3)
                )
                .uri("lb://service-b"))
            
            // === ROUTE POUR SERVICE C ===
            .route("service-c", r -> r
                .path("/api/service-c/**")
                .filters(f -> f
                    .stripPrefix(2)
                    .addResponseHeader("X-Response-Service", "Service-C")
                    .retry(3)
                )
                .uri("lb://service-c"))
            
            // .build() = construit et retourne l'objet RouteLocator
            .build();
    }
}

Avantages de la configuration Java :

  • Plus de puissance : Logique conditionnelle, boucles, etc.
  • Meilleur typage : Erreurs détectées à la compilation
  • Plus lisible : Structure hiérarchique claire
  • Plus maintenable : Refactoring facile avec l'IDE

5 Classe principale du Gateway - Explication complète

Créer la classe ApiGatewayApplication.java :

Chemin : src/main/java/com/example/apigateway/ApiGatewayApplication.java

// Annotation Spring Boot qui combine plusieurs fonctionnalités :
// @Configuration + @EnableAutoConfiguration + @ComponentScan
@SpringBootApplication

// Active le client Eureka pour la découverte de services
// Permet à ce service de s'enregistrer et de trouver les autres services
@EnableDiscoveryClient

// Classe principale de l'application
public class ApiGatewayApplication {
    
    // Méthode principale - point d'entrée de l'application
    public static void main(String[] args) {
        // SpringApplication.run() :
        // 1. Crée le contexte Spring (conteneur d'inversion de contrôle)
        // 2. Scanne les composants (@Component, @Service, @Repository, etc.)
        // 3. Configure automatiquement les beans selon les dépendances
        // 4. Démarre le serveur web embarqué (Tomcat par défaut)
        // 5. Lance l'application
        SpringApplication.run(ApiGatewayApplication.class, args);
    }
}

Comprendre les annotations :

@SpringBootApplication :

  • Équivalent à @Configuration + @EnableAutoConfiguration + @ComponentScan
  • @Configuration : Cette classe peut définir des beans
  • @EnableAutoConfiguration : Configure automatiquement Spring selon les dépendances
  • @ComponentScan : Scanne les composants dans le package et ses sous-packages

@EnableDiscoveryClient :

  • Active la découverte de services via Eureka
  • Permet à ce service de s'enregistrer automatiquement
  • Permet à ce service de découvrir les autres services

Processus de démarrage détaillé :

  1. JVM démarre : Charge la classe ApiGatewayApplication
  2. main() appelée : Point d'entrée de l'application
  3. SpringApplication.run() :
    • Crée le contexte Spring (ApplicationContext)
    • Lit le fichier application.properties
    • Scanne les annotations (@Component, @Service, etc.)
    • Crée et configure tous les beans nécessaires
    • Configure le serveur web (Tomcat, Netty, etc.)
    • Démarre le serveur sur le port configuré (8080)
    • Enregistre ce service auprès d'Eureka
    • Commence à écouter les requêtes HTTP

Partie 2 : Configuration Avancée des Routes - Explication pas à pas

1 Routes avec filtres avancés - Configuration détaillée

Ajout de filtres complexes dans application.properties :

# === ROUTE AVANCÉE POUR SERVICE A AVEC FILTRES COMPLEXES ===
# ID unique de la route
spring.cloud.gateway.routes[0].id=service-a-advanced

# URI de destination avec load balancing
spring.cloud.gateway.routes[0].uri=lb://service-a

# === PRÉDICATS (CONDITIONS DE ROUTAGE) ===
# Predicate 0 : Chemin de l'URL
# /api/v1/service-a/** = tous les chemins commençant par /api/v1/service-a/
spring.cloud.gateway.routes[0].predicates[0]=Path=/api/v1/service-a/**

# Predicate 1 : Méthode HTTP autorisée
# Seules les requêtes GET et POST seront routées
spring.cloud.gateway.routes[0].predicates[1]=Method=GET,POST

# Predicate 2 : Header requis
# La requête doit avoir un header X-API-Version avec la valeur 1.0
spring.cloud.gateway.routes[0].predicates[2]=Header=X-API-Version, 1.0

# === FILTRES À APPLIQUER ===
# Filtre 0 : StripPrefix
# Retire les 3 premiers segments du chemin (/api/v1/service-a/health → /health)
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=3

# Filtre 1 : AddRequestHeader
# Ajoute un header à la requête envoyée au service
spring.cloud.gateway.routes[0].filters[1]=AddRequestHeader=X-Forwarded-By, API-Gateway

# Filtre 2 : AddResponseHeader avec expression SpEL
# Ajoute un header à la réponse avec la date courante
# #{new java.util.Date()} = expression Spring Expression Language
spring.cloud.gateway.routes[0].filters[2]=AddResponseHeader=X-Response-Time, #{new java.util.Date()}

# Filtre 3 : Retry
# Réessaie la requête en cas d'erreurs spécifiques
# 3 = nombre de tentatives
# INTERNAL_SERVER_ERROR, SERVICE_UNAVAILABLE = codes d'erreur qui déclenchent le retry
spring.cloud.gateway.routes[0].filters[3]=Retry=3,INTERNAL_SERVER_ERROR,SERVICE_UNAVAILABLE


# === ROUTE POUR SERVICE B AVEC RATE LIMITING ===
spring.cloud.gateway.routes[1].id=service-b-limited
spring.cloud.gateway.routes[1].uri=lb://service-b
spring.cloud.gateway.routes[1].predicates[0]=Path=/api/v1/service-b/**
# Rate limiting : 10 requêtes par seconde, rafale de 20
spring.cloud.gateway.routes[1].filters[0]=StripPrefix=3
spring.cloud.gateway.routes[1].filters[1]=RequestRateLimiter=10,20


# === ROUTE POUR SERVICE C AVEC FALLBACK ===
spring.cloud.gateway.routes[2].id=service-c-fallback
spring.cloud.gateway.routes[2].uri=lb://service-c
spring.cloud.gateway.routes[2].predicates[0]=Path=/api/v1/service-c/**
spring.cloud.gateway.routes[2].filters[0]=StripPrefix=3
# FallbackHeaders : ajoute des headers en cas d'erreur pour le fallback
spring.cloud.gateway.routes[2].filters[1]=FallbackHeaders=ExecutionException

Comprendre chaque filtre :

Path=/api/v1/service-a/** :

  • Route toutes les requêtes dont le chemin commence par /api/v1/service-a/
  • ** = n'importe quel sous-chemin
  • Exemple : /api/v1/service-a/users/123 matchera

Method=GET,POST :

  • Seules les requêtes GET et POST seront routées
  • Les PUT, DELETE, etc. donneront une 404
  • Utile pour restreindre les opérations autorisées

Header=X-API-Version, 1.0 :

  • La requête doit avoir un header X-API-Version avec la valeur 1.0
  • Utile pour le versioning d'API
  • Requêtes sans ce header ou avec une autre valeur seront rejetées

Retry=3,INTERNAL_SERVER_ERROR,SERVICE_UNAVAILABLE :

  • En cas d'erreurs 500 ou 503, réessaie jusqu'à 3 fois
  • Utile pour la résilience face aux pannes temporaires
  • Chaque tentative est espacée de manière exponentielle

2 Filtres globaux personnalisés - Explication complète

Créer des filtres qui s'appliquent à toutes les routes :

Chemin : src/main/java/com/example/apigateway/config/GlobalFiltersConfig.java

// Annotation pour dire que c'est une classe de configuration Spring
@Configuration
public class GlobalFiltersConfig {
    
    // @Bean pour créer un bean Spring
    // @Order(-1) pour que ce filtre s'exécute en premier (-1 = avant les autres)
    @Bean
    @Order(-1)
    public GlobalFilter preGlobalFilter() {
        // Retourne un filtre global
        return (exchange, chain) -> {
            // exchange = contient toutes les informations de la requête/réponse
            // chain = chaîne des filtres suivants
            
            // === LOGIQUE DU FILTRE PRE-PROCESSING ===
            System.out.println("=== FILTRE PRÉ-TRAITEMENT EXÉCUTÉ ===");
            
            // Affiche le chemin de la requête
            System.out.println("Chemin de la requête: " + exchange.getRequest().getPath());
            
            // Affiche la méthode HTTP
            System.out.println("Méthode HTTP: " + exchange.getRequest().getMethod());
            
            // === MODIFICATION DE LA REQUÊTE ===
            // .mutate() = permet de modifier la requête de manière immutable
            ServerHttpRequest request = exchange.getRequest()
                .mutate()
                // Ajoute un header personnalisé avec un UUID unique
                .header("X-Global-Request-ID", java.util.UUID.randomUUID().toString())
                // Ajoute un header avec le timestamp
                .header("X-Request-Time", String.valueOf(System.currentTimeMillis()))
                // .build() = construit la nouvelle requête modifiée
                .build();
            
            // === CONTINUER LA CHAÎNE DE FILTRES ===
            // exchange.mutate().request(request).build() = met à jour l'échange avec la requête modifiée
            // chain.filter() = continue vers le filtre suivant
            return chain.filter(exchange.mutate().request(request).build());
        };
    }
    
    // Deuxième filtre global, exécuté après le premier (@Order(0))
    @Bean
    @Order(0)
    public GlobalFilter postGlobalFilter() {
        return (exchange, chain) -> {
            // === EXÉCUTION ASYNCHRONE AVEC MONO ===
            // chain.filter(exchange) = exécute les filtres suivants
            // .then() = exécute du code après que tous les filtres suivants ont terminé
            return chain.filter(exchange).then(
                // Mono.fromRunnable() = crée un Mono (programmation réactive) à partir d'un Runnable
                Mono.fromRunnable(() -> {
                    // === LOGIQUE DU FILTRE POST-TRAITEMENT ===
                    System.out.println("=== FILTRE POST-TRAITEMENT EXÉCUTÉ ===");
                    
                    // Affiche le statut de la réponse
                    System.out.println("Statut de la réponse: " + exchange.getResponse().getStatusCode());
                    
                    // Ajoute un header à la réponse
                    exchange.getResponse().getHeaders().add("X-Global-Response", "Processed by Gateway");
                })
            );
        };
    }
}

Comprendre la programmation réactive avec Mono :

Spring Cloud Gateway est basé sur Project Reactor, qui utilise des Flux et Mono.

Mono<T> :

  • Représente une opération asynchrone qui produit 0 ou 1 résultat
  • Équivalent à Promise en JavaScript ou Future en Java classique
  • Non-bloquant : ne bloque pas le thread d'exécution

chain.filter(exchange).then(Mono.fromRunnable(...)) :

  • chain.filter(exchange) = exécute les filtres suivants
  • .then(...) = exécute le code après que tout est terminé
  • Permet de faire du post-traitement sans bloquer

Ordre d'exécution des filtres :

1. preGlobalFilter() (@Order(-1)) - PRÉ-TRAITEMENT
   ↓
2. Filtres de route configurés
   ↓
3. Appel au service cible
   ↓
4. Réponse du service
   ↓
5. Filtres de route configurés (sens inverse)
   ↓
6. postGlobalFilter() (@Order(0)) - POST-TRAITEMENT

3 Configuration de la sécurité - Explication pas à pas

Ajouter la dépendance de sécurité dans pom.xml :

<!-- Ajouter cette dépendance dans la section <dependencies> -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Cette dépendance ajoute Spring Security, qui fournit :

  • Authentification (vérification d'identité)
  • Autorisation (vérification des permissions)
  • Protection contre les attaques (CSRF, XSS, etc.)

Créer la configuration de sécurité :

Chemin : src/main/java/com/example/apigateway/config/SecurityConfig.java

// Annotation pour dire que c'est une configuration
@Configuration

// Active la sécurité Web réactive (nécessaire pour Spring Cloud Gateway)
@EnableWebFluxSecurity
public class SecurityConfig {
    
    // @Bean pour créer un bean Spring géré
    // SecurityWebFilterChain = chaîne de filtres de sécurité
    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        // Configuration de la sécurité HTTP
        http
            // .authorizeExchange() = configure l'autorisation des échanges
            .authorizeExchange(exchanges -> exchanges
                // .pathMatchers() = définit des motifs de chemins
                // .permitAll() = autorise tout le monde (pas d'authentification requise)
                
                // Autoriser l'accès aux endpoints Actuator (monitoring)
                .pathMatchers("/actuator/**").permitAll()
                
                // Autoriser l'accès aux APIs publiques
                .pathMatchers("/api/public/**").permitAll()
                
                // Pour toutes les autres requêtes, authentification requise
                .anyExchange().authenticated()
            )
            // .httpBasic() = active l'authentification HTTP Basic
            // Affiche une popup de login dans le navigateur
            .httpBasic()
            
            // .and() = permet d'enchaîner les configurations
            // .csrf().disable() = désactive la protection CSRF
            // (souvent désactivé pour les APIs REST stateless)
            .and()
            .csrf().disable();
        
        // Retourne la chaîne de filtres de sécurité configurée
        return http.build();
    }
}

Comprendre l'authentification HTTP Basic :

L'authentification HTTP Basic est la méthode la plus simple :

  1. Le navigateur affiche une popup de login
  2. L'utilisateur entre username/password
  3. Les identifiants sont encodés en Base64 et envoyés dans un header
  4. Le serveur vérifie les identifiants
  5. Header d'authentification :

    Authorization: Basic dXNlcjpwYXNzd29yZA==

    dXNlcjpwYXNzd29yZA== est user:password encodé en Base64

Tester la sécurité :

# Accès sans authentification - devrait être refusé
curl http://localhost:8080/api/service-a/health

# Accès avec authentification - devrait fonctionner
curl -u user:password http://localhost:8080/api/service-a/health

# Accès aux endpoints publics - devrait fonctionner
curl http://localhost:8080/api/public/info

# Accès aux endpoints Actuator - devrait fonctionner
curl http://localhost:8080/actuator/health

Partie 3 : Configuration des Filtres Avancés - Explication détaillée

1 Filtres personnalisés - Création pas à pas

Pourquoi créer des filtres personnalisés ?

Les filtres intégrés ne couvrent pas tous les besoins. Les filtres personnalisés permettent :

  • Ajout de headers spécifiques à votre application
  • Transformation de données selon vos règles
  • Logging personnalisé
  • Validation spécifique

Créer un filtre personnalisé pour la transformation de requêtes :

Chemin : src/main/java/com/example/apigateway/filters/CustomRequestFilterFactory.java

// Annotation pour que Spring gère ce composant
@Component

// Hérite d'AbstractGatewayFilterFactory pour créer un filtre configurable
// <CustomRequestFilterFactory.Config> = type de configuration du filtre
public class CustomRequestFilterFactory extends AbstractGatewayFilterFactory<CustomRequestFilterFactory.Config> {
    
    // Constructeur qui définit le type de configuration
    public CustomRequestFilterFactory() {
        // super(Config.class) = indique que la configuration est de type Config
        super(Config.class);
    }
    
    // Méthode principale qui applique le filtre
    @Override
    public GatewayFilter apply(Config config) {
        // Retourne un GatewayFilter (filtre de Gateway)
        return (exchange, chain) -> {
            // === TRANSFORMATION DE LA REQUÊTE ===
            
            // exchange.getRequest() = obtient la requête actuelle
            // .mutate() = permet de la modifier de manière immutable
            ServerHttpRequest request = exchange.getRequest()
                .mutate()
                // Ajoute un header personnalisé avec la valeur configurée
                .header("X-Custom-Header", config.getHeaderValue())
                // Ajoute un header avec le timestamp de la requête
                .header("X-Request-Time", String.valueOf(System.currentTimeMillis()))
                // .build() = construit la nouvelle requête modifiée
                .build();
            
            // === CONTINUER LA CHAÎNE DE TRAITEMENT ===
            // exchange.mutate().request(request).build() = met à jour l'échange
            // chain.filter() = continue vers le filtre suivant
            return chain.filter(exchange.mutate().request(request).build());
        };
    }
    
    // Classe de configuration interne pour ce filtre
    // Permet de configurer le filtre via application.properties
    public static class Config {
        // Propriété configurable
        private String headerValue;
        
        // Getter
        public String getHeaderValue() {
            return headerValue;
        }
        
        // Setter
        public void setHeaderValue(String headerValue) {
            this.headerValue = headerValue;
        }
    }
}

Utiliser le filtre personnalisé dans la configuration :

# Utilisation du filtre personnalisé dans application.properties

# Créer une route qui utilise le filtre personnalisé
spring.cloud.gateway.routes[0].id=service-a-custom
spring.cloud.gateway.routes[0].uri=lb://service-a

# Prédicat : quand le chemin commence par /api/custom/
spring.cloud.gateway.routes[0].predicates[0]=Path=/api/custom/**

# Filtres à appliquer :
# 1. StripPrefix=2 : retire /api/custom/ du chemin
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=2

# 2. CustomRequest=CustomValue : utilise notre filtre personnalisé
# CustomRequest = nom du filtre (déduit du nom de la classe)
# CustomValue = valeur passée à setHeaderValue()
spring.cloud.gateway.routes[0].filters[1]=CustomRequest=CustomValue

Comment Spring déduit le nom du filtre :

  • Classe : CustomRequestFilterFactory
  • Nom du filtre : CustomRequest (enlève "FilterFactory")
  • Utilisation : CustomRequest=valeur

2 Filtres de rate limiting avec Redis - Explication complète

Qu'est-ce que le rate limiting ?

Le rate limiting limite le nombre de requêtes qu'un client peut faire dans une période donnée. C'est essentiel pour :

  • Protéger contre les attaques DDoS
  • Éviter la surcharge du système
  • Fair usage entre les clients

Ajouter la dépendance Redis dans pom.xml :

<!-- Ajouter cette dépendance pour Redis réactif -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

Configuration Redis dans application.properties :

# === CONFIGURATION REDIS POUR RATE LIMITING ===
# Adresse du serveur Redis
spring.redis.host=localhost

# Port Redis (par défaut 6379)
spring.redis.port=6379

# === CONFIGURATION DU RATE LIMITING SUR UNE ROUTE ===
# ID de la route
spring.cloud.gateway.routes[0].id=service-a-rate-limited

# URI de destination
spring.cloud.gateway.routes[0].uri=lb://service-a

# Prédicat : chemin
spring.cloud.gateway.routes[0].predicates[0]=Path=/api/rate-limited/**

# Filtres :
# 1. StripPrefix=2
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=2

# 2. RequestRateLimiter=10,20
# 10 = nombre de requêtes par seconde autorisées
# 20 = rafale maximale (burst capacity)
spring.cloud.gateway.routes[0].filters[1]=RequestRateLimiter=10,20,#@redisRateLimiter

Configuration du rate limiter dans Java :

Chemin : src/main/java/com/example/apigateway/config/RateLimiterConfig.java

@Configuration
public class RateLimiterConfig {
    
    // @Bean pour créer un bean Spring
    // @Primary pour indiquer que c'est le KeyResolver par défaut
    @Bean
    @Primary
    // KeyResolver = détermine comment identifier un client unique
    KeyResolver userKeyResolver() {
        // Retourne un KeyResolver qui utilise le paramètre "user" de la requête
        return exchange -> {
            // exchange = contient les informations de la requête
            
            // Récupère le paramètre "user" de la requête
            String user = exchange.getRequest().getQueryParams().getFirst("user");
            
            // Mono.just() = crée un Mono qui émet immédiatement une valeur
            // Si user est null, utilise "anonymous"
            return Mono.just(user != null ? user : "anonymous");
        };
    }
    
    // KeyResolver basé sur l'IP du client
    @Bean
    KeyResolver ipKeyResolver() {
        return exchange -> {
            // Récupère l'adresse IP du client
            String ip = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
            return Mono.just(ip);
        };
    }
    
    // Configuration du RedisRateLimiter
    @Bean
    RedisRateLimiter redisRateLimiter() {
        // 10 requêtes par seconde, rafale de 20
        return new RedisRateLimiter(10, 20);
    }
}

Comprendre le fonctionnement du rate limiting :

  1. Identification du client : KeyResolver détermine qui fait la requête
  2. Stockage des compteurs : Redis stocke le nombre de requêtes par client
  3. Vérification : Pour chaque requête, on vérifie si la limite est atteinte
  4. Action : Si limite dépassée, on retourne 429 Too Many Requests

Tester le rate limiting :

# Installer Redis (sur Ubuntu/Debian)
sudo apt-get install redis-server

# Démarrer Redis
redis-server

# Tester le rate limiting
# Ces 10 requêtes devraient réussir
for i in {1..10}; do 
    curl "http://localhost:8080/api/rate-limited/health?user=testuser"
done

# Ces requêtes supplémentaires devraient échouer avec 429 Too Many Requests
for i in {1..5}; do 
    curl "http://localhost:8080/api/rate-limited/health?user=testuser"
done

Partie 4 : Tester l'Architecture Gateway - Guide pas à pas

1 Démarrer l'ensemble du système - Ordre détaillé

Ordre critique de démarrage :

IMPORTANT : L'ordre de démarrage est crucial pour le bon fonctionnement !

  1. Eureka Server (Port 8761) : Doit démarrer en premier car c'est l'annuaire
  2. Microservices (Ports 8081, 8082, 8083) : S'enregistrent auprès d'Eureka
  3. API Gateway (Port 8080) : Découvre les services et configure les routes

Démarrer Eureka Server :

# Ouvrir un terminal
cd chemin/vers/eureka-server

# Démarrer avec Maven
mvn spring-boot:run

# OU si vous avez le JAR
java -jar target/eureka-server-0.0.1-SNAPSHOT.jar

# Vérifier : http://localhost:8761

Démarrer les microservices :

# Terminal 2 : Service A
cd chemin/vers/service-a
mvn spring-boot:run

# Terminal 3 : Service B  
cd chemin/vers/service-b
mvn spring-boot:run

# Terminal 4 : Service C
cd chemin/vers/service-c
mvn spring-boot:run

# Vérifier dans Eureka : http://localhost:8761
# Vous devriez voir les 3 services enregistrés

Démarrer API Gateway :

# Terminal 5 : API Gateway
cd chemin/vers/api-gateway
mvn spring-boot:run

# OU avec le JAR
java -jar target/api-gateway-0.0.1-SNAPSHOT.jar

# Vérifier : http://localhost:8080/actuator/health

2 Vérifier l'enregistrement dans Eureka - Explication détaillée

Accéder à l'interface web d'Eureka :

# Ouvrir dans un navigateur web
http://localhost:8761

Ce que vous devriez voir dans l'interface Eureka :

=== SYSTEM STATUS ===
Environment: development
Data center: default

=== INSTANCES CURRENTLY REGISTERED WITH EUREKA ===
Application    AMIs    Availability Zones    Status
─────────────────────────────────────────────────────────────
API-GATEWAY    n/a     (1)                  UP (1) - hostname:api-gateway:8080
SERVICE-A      n/a     (1)                  UP (1) - hostname:service-a:8081
SERVICE-B      n/a     (1)                  UP (1) - hostname:service-b:8082
SERVICE-C      n/a     (1)                  UP (1) - hostname:service-c:8083

=== GENERAL INFO ===
Total number of instances: 4
Renews threshold: 6
Renews (last min): 6

Signification des statuts :

  • UP : Service en cours d'exécution et en bonne santé
  • DOWN : Service arrêté ou en erreur
  • STARTING : Service en cours de démarrage
  • OUT_OF_SERVICE : Service temporairement indisponible

Vérifier via l'API Eureka :

# Liste de tous les services enregistrés
curl http://localhost:8761/eureka/apps

# Détails d'un service spécifique
curl http://localhost:8761/eureka/apps/SERVICE-A

# Informations sur cette instance
curl http://localhost:8761/eureka/apps/SERVICE-A/service-a:8081

3 Tester les routes du Gateway - Guide complet

Routes automatiques via discovery locator :

Quand spring.cloud.gateway.discovery.locator.enabled=true, Gateway crée automatiquement des routes pour chaque service enregistré dans Eureka.

# === ROUTES AUTOMATIQUES (NOMS EN MAJUSCULES) ===

# Accéder à Service A via le Gateway
# Format : http://gateway:port/NOM-SERVICE/endpoint
curl http://localhost:8080/SERVICE-A/health
curl http://localhost:8080/SERVICE-A/info

# Accéder à Service B via le Gateway
curl http://localhost:8080/SERVICE-B/health
curl http://localhost:8080/SERVICE-B/users

# Accéder à Service C via le Gateway
curl http://localhost:8080/SERVICE-C/health
curl http://localhost:8080/SERVICE-C/data

Routes manuelles configurées :

Les routes définies dans application.properties ou via Java.

# === ROUTES MANUELLES (CONFIGURÉES) ===

# Accéder à Service A via route personnalisée
# Format : http://gateway:port/api/service-name/endpoint
curl http://localhost:8080/api/service-a/health
curl http://localhost:8080/api/service-a/info

# Accéder à Service B via route personnalisée
curl http://localhost:8080/api/service-b/health
curl http://localhost:8080/api/service-b/users

# Accéder à Service C via route personnalisée
curl http://localhost:8080/api/service-c/health
curl http://localhost:8080/api/service-c/data

Différence entre routes automatiques et manuelles :

Aspect Routes Automatiques Routes Manuelles
Configuration Automatique via Eureka Manuelle dans config
Nommage NOM-SERVICE (majuscules) /api/service-name (configurable)
Filtres Aucun filtre par défaut Filtres personnalisables
Flexibilité Limitée Très flexible

4 Tester les filtres et fonctionnalités avancées

Tester les filtres personnalisés :

# Tester route avec filtre personnalisé
curl http://localhost:8080/api/custom/health

# Vérifier les headers ajoutés
curl -v http://localhost:8080/api/custom/health

# Dans la réponse, vous devriez voir :
# < X-Custom-Header: CustomValue
# < X-Request-Time: 1234567890123

Tester le rate limiting :

# Démarrer Redis (nécessaire pour rate limiting)
redis-server

# Tester rate limiting
# Ces 10 requêtes devraient réussir (200 OK)
for i in {1..10}; do 
    echo "Requête $i:"
    curl -w "%{http_code}\n" "http://localhost:8080/api/rate-limited/health?user=testuser"
    sleep 0.1
done

# Ces requêtes supplémentaires devraient échouer (429 Too Many Requests)
for i in {1..5}; do 
    echo "Requête supplémentaire $i:"
    curl -w "%{http_code}\n" "http://localhost:8080/api/rate-limited/health?user=testuser"
    sleep 0.1
done

Tester la sécurité :

# Tester routes protégées (devrait demander authentification)
curl http://localhost:8080/api/protected/endpoint
# Résultat : 401 Unauthorized

# Tester avec authentification correcte
curl -u user:password http://localhost:8080/api/service-a/health
# Résultat : 200 OK + réponse du service

# Tester routes publiques (devrait fonctionner sans auth)
curl http://localhost:8080/api/public/info
# Résultat : 200 OK

5 Tester la résilience et le load balancing

Simuler une panne de service :

# === SIMULATION DE PANNE ===

# 1. Dans un terminal, arrêter Service B (Ctrl+C)

# 2. Attendre 30-90 secondes (temps d'expiration Eureka)

# 3. Vérifier dans Eureka : http://localhost:8761
# Service B devrait disparaître de la liste

# 4. Essayer d'accéder à Service B via Gateway
curl http://localhost:8080/SERVICE-B/health
# Résultat attendu : 503 Service Unavailable

# 5. Essayer d'accéder aux autres services (devraient fonctionner)
curl http://localhost:8080/SERVICE-A/health
# Résultat attendu : 200 OK

Tester le retry mechanism :

# Tester route avec retry configuré
curl http://localhost:8080/api/service-a/health

# Pour tester le retry, vous pouvez :
# 1. Temporairement arrêter un service
# 2. Faire une requête pendant l'arrêt
# 3. Redémarrer rapidement le service
# 4. Observer que le Gateway réessaie automatiquement

Comportement du load balancing :

Si vous avez plusieurs instances du même service, Gateway distribue automatiquement les requêtes.

# Exemple avec 2 instances de Service A :
# Instance 1 : port 8081
# Instance 2 : port 8084 (même service, port différent)

# Gateway alterne automatiquement entre les instances
curl http://localhost:8080/SERVICE-A/health  # → Instance 1
curl http://localhost:8080/SERVICE-A/health  # → Instance 2
curl http://localhost:8080/SERVICE-A/health  # → Instance 1

Partie 5 : Monitoring et Actuator - Explication complète

1 Configuration des endpoints Actuator - Détails

Qu'est-ce que Spring Boot Actuator ?

Actuator fournit des endpoints de production-ready pour :

  • Health : État de santé de l'application
  • Info : Informations générales
  • Metrics : Métriques de performance
  • Env : Variables d'environnement
  • Gateway : Informations spécifiques au Gateway

Configuration complète dans application.properties :

# === CONFIGURATION DES ENDPOINTS ACTUATOR ===

# Exposition des endpoints
# gateway = routes et configuration du Gateway
# health = état de santé
# info = informations générales
# metrics = métriques de performance
# env = variables d'environnement
# configprops = propriétés de configuration
# loggers = configuration des logs
management.endpoints.web.exposure.include=gateway,health,info,metrics,env,configprops,loggers

# Afficher les détails de santé même pour les endpoints non sensibles
management.endpoint.health.show-details=always

# Activer l'endpoint gateway (pour voir les routes)
management.endpoint.gateway.enabled=true

# === CONFIGURATION DES MÉTRIQUES ===
# Activer l'export vers Prometheus
management.metrics.export.prometheus.enabled=true

# Activer le timing automatique pour les requêtes web
management.metrics.web.server.request.autotime.enabled=true

# === CONFIGURATION DES LOGS ===
# Niveau de log pour le Gateway
logging.level.org.springframework.cloud.gateway=INFO

# Niveau de log pour le client HTTP réactif
logging.level.reactor.netty=INFO

# Pattern de log personnalisé
logging.pattern.console=%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n

2 Utiliser les endpoints Actuator - Guide pratique

Endpoints disponibles et leur utilité :

# === ENDPOINTS ACTUATOR UTILES ===

# 1. Informations générales
curl http://localhost:8080/actuator/info
# Résultat : Informations sur l'application, version, etc.

# 2. Santé de l'application
curl http://localhost:8080/actuator/health
# Résultat : {"status":"UP"} si tout va bien

# 3. Routes configurées
curl http://localhost:8080/actuator/gateway/routes
# Résultat : Liste de toutes les routes configurées

# 4. Refresh des routes (recharge la configuration)
curl -X POST http://localhost:8080/actuator/gateway/refresh
# Résultat : 200 OK, routes rechargées

# 5. Métriques
curl http://localhost:8080/actuator/metrics
# Résultat : Liste de toutes les métriques disponibles

# 6. Métrique spécifique
curl http://localhost:8080/actuator/metrics/gateway.requests
# Résultat : Statistiques sur les requêtes du Gateway

# 7. Configuration actuelle
curl http://localhost:8080/actuator/configprops
# Résultat : Toutes les propriétés de configuration chargées

# 8. Variables d'environnement
curl http://localhost:8080/actuator/env
# Résultat : Variables d'environnement et propriétés

Exemple détaillé de réponse des routes :

[
  {
    "predicate": "Paths: [/api/service-a/**], match trailing slash: true",
    "metadata": {
      "id": "service-a"
    },
    "route_id": "service-a",
    "filters": [
      "[[StripPrefix parts = 2], order = 0]"
    ],
    "uri": "lb://service-a",
    "order": 0
  },
  {
    "predicate": "Paths: [/api/service-b/**], match trailing slash: true",
    "metadata": {
      "id": "service-b"
    },
    "route_id": "service-b",
    "filters": [
      "[[StripPrefix parts = 2], order = 0]"
    ],
    "uri": "lb://service-b",
    "order": 0
  }
]

Comprendre la structure de la réponse :

  • predicate : Condition de routage (chemin, méthode, etc.)
  • route_id : Identifiant unique de la route
  • filters : Filtres appliqués à cette route
  • uri : Destination (lb:// = load balancing)
  • order : Ordre d'évaluation des routes

3 Monitoring avancé avec Prometheus et Grafana

Configuration pour Prometheus :

# Dans application.properties

# Activer les métriques Prometheus
management.endpoints.web.exposure.include=prometheus
management.metrics.export.prometheus.enabled=true

# Endpoint Prometheus
management.endpoint.prometheus.enabled=true

# Configuration des métriques
management.metrics.web.server.request.autotime.enabled=true
management.metrics.distribution.percentiles-histogram.http.server.requests=true

Accéder aux métriques Prometheus :

# Endpoint Prometheus
curl http://localhost:8080/actuator/prometheus

# Exemple de métriques retournées :
# gateway_requests_seconds_count{routeId="service-a",outcome="SUCCESSFUL",status="200",} 5.0
# gateway_requests_seconds_sum{routeId="service-a",outcome="SUCCESSFUL",status="200",} 0.123
# jvm_memory_used_bytes{area="heap",id="G1 Eden Space",} 1.23456789E8

Meilleures Pratiques et Améliorations - Guide détaillé

1 Structure de configuration optimale

graph TD A[Client] --> B[API Gateway] B --> C[Eureka Server] C --> D[Service Registry] D --> E[Service A] D --> F[Service B] D --> G[Service C] B --> H[Redis - Rate Limiting] B --> I[Security - Auth Server] B --> J[Monitoring - Prometheus] K[Load Balancer] --> B style B fill:#4CAF50 style C fill:#2196F3 style D fill:#2196F3 style E fill:#FF9800 style F fill:#FF9800 style G fill:#FF9800 style H fill:#9C27B0 style I fill:#F44336 style J fill:#FF9800 style K fill:#607D8B

Architecture de production recommandée :

  • Load Balancer : Devant le Gateway pour haute disponibilité
  • Multiple Gateways : Pour scalabilité
  • Redis Cluster : Pour rate limiting distribué
  • Auth Server : Service d'authentification centralisé
  • Monitoring Stack : Prometheus + Grafana + AlertManager

2 Sécurité avancée - Configuration détaillée

Sécurité complète avec JWT :

# === CONFIGURATION SÉCURITÉ AVANCÉE ===

# Configuration CORS (Cross-Origin Resource Sharing)
# Autoriser toutes les origines (à restreindre en production)
spring.cloud.gateway.globalcors.cors-configurations.[/**].allowed-origins=*
# Méthodes HTTP autorisées
spring.cloud.gateway.globalcors.cors-configurations.[/**].allowed-methods=GET,POST,PUT,DELETE,OPTIONS
# Headers autorisés
spring.cloud.gateway.globalcors.cors-configurations.[/**].allowed-headers=*
# Autoriser les credentials
spring.cloud.gateway.globalcors.cors-configurations.[/**].allow-credentials=true

# Configuration HTTPS
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=password
server.ssl.keyStoreType=PKCS12
server.ssl.keyAlias=tomcat

# Redirection HTTP vers HTTPS
server.http.port=8080
server.port=8443

Configuration JWT dans SecurityConfig :

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http
            .authorizeExchange(exchanges -> exchanges
                .pathMatchers("/actuator/**").permitAll()
                .pathMatchers("/api/public/**").permitAll()
                .pathMatchers("/api/v1/auth/**").permitAll()
                .anyExchange().authenticated()
            )
            // Désactiver HTTP Basic
            .httpBasic().disable()
            // Activer JWT
            .oauth2ResourceServer(ServerHttpSecurity.OAuth2ResourceServerSpec::jwt)
            .csrf().disable();
        return http.build();
    }
}

3 Performance et monitoring - Optimisations

Configuration de performance optimale :

# === CONFIGURATION PERFORMANCE ===

# Compression HTTP
server.compression.enabled=true
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain
server.compression.min-response-size=1024

# Configuration du client HTTP
# Timeout de connexion (5 secondes)
spring.cloud.gateway.httpclient.connect-timeout=5000
# Timeout de réponse (10 secondes)
spring.cloud.gateway.httpclient.response-timeout=10s
# Configuration du pool de connexions
spring.cloud.gateway.httpclient.pool.max-idle-time=60000
spring.cloud.gateway.httpclient.pool.max-life-time=60000
spring.cloud.gateway.httpclient.pool.max-connections=1000

# Configuration du cache
spring.cloud.gateway.routes[0].filters[2]=CacheRequestBody=10MB

# Configuration des threads
server.tomcat.max-threads=200
server.tomcat.min-spare-threads=10

4 Gestion des erreurs et fallback - Configuration robuste

Configuration robuste avec fallback :

# === CONFIGURATION DES ERREURS ===

# Configuration des fallbacks
spring.cloud.gateway.routes[0].filters[3]=FallbackHeaders=ExecutionException

# Configuration des statuts d'erreur personnalisés
spring.cloud.gateway.routes[0].filters[4]=SetStatus=BAD_GATEWAY

# Configuration des retries avancés
# 3 tentatives, avec codes d'erreur spécifiques
spring.cloud.gateway.routes[0].filters[5]=Retry=3,INTERNAL_SERVER_ERROR,SERVICE_UNAVAILABLE,GATEWAY_TIMEOUT,IOException

# Timeout global pour les routes
spring.cloud.gateway.routes[0].metadata.timeout=15000

# Configuration du circuit breaker (nécessite Resilience4j)
spring.cloud.gateway.routes[0].filters[6]=CircuitBreaker=myCircuitBreaker

Gestionnaire d'erreurs global :

@Configuration
// @Order(-2) pour qu'il s'exécute avant les autres gestionnaires
@Order(-2)
public class GlobalErrorWebExceptionHandler implements ErrorWebExceptionHandler {
    
    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        // Log l'erreur
        ex.printStackTrace();
        
        // Définit le statut HTTP
        exchange.getResponse().setStatusCode(HttpStatus.BAD_GATEWAY);
        
        // Optionnel : ajoute un message d'erreur personnalisé
        String errorMessage = "{\"error\":\"Gateway Error\",\"message\":\"" + ex.getMessage() + "\"}";
        exchange.getResponse().getHeaders().add("Content-Type", "application/json");
        
        // Écrit le message d'erreur dans le corps de la réponse
        DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(errorMessage.getBytes());
        return exchange.getResponse().writeWith(Mono.just(buffer));
    }
}

Dépannage Courant pour Débutants - Guide pas à pas

1 Routes non trouvées - Diagnostic complet

Symptôme :

"404 Not Found" pour les routes configurées

Causes possibles et solutions :

1. Vérifier les routes configurées :

# Vérifier les routes actuelles
curl http://localhost:8080/actuator/gateway/routes

# Résultat attendu : Liste des routes configurées
# Si la liste est vide, les routes ne sont pas chargées

2. Vérifier l'enregistrement dans Eureka :

# Vérifier Eureka
curl http://localhost:8761/eureka/apps

# Vérifier un service spécifique
curl http://localhost:8761/eureka/apps/SERVICE-A

3. Vérifier la configuration des routes :

# Dans application.properties, vérifier :
spring.cloud.gateway.routes[0].id=service-a
spring.cloud.gateway.routes[0].uri=lb://service-a  # Bon nom de service ?
spring.cloud.gateway.routes[0].predicates[0]=Path=/api/service-a/**  # Bon chemin ?

4. Vérifier les logs du Gateway :

# Dans les logs, chercher :
# "RoutePredicateHandlerMapping" - pour le routage
# "LoadBalancerClientFilter" - pour le load balancing
# "NotFoundException" - pour les routes non trouvées

2 Erreurs de load balancing - Guide de résolution

Symptôme :

"503 Service Unavailable" ou "Unable to find instance"

Causes possibles et solutions :

1. Service non enregistré dans Eureka :

# Vérifier que le service est dans Eureka
curl http://localhost:8761/eureka/apps/SERVICE-A

# Si réponse vide : le service n'est pas enregistré
# Vérifier les logs du service : doit afficher "Registering application SERVICE-A"

2. Mauvais nom de service dans l'URI :

# ERREUR :
spring.cloud.gateway.routes[0].uri=lb://ServiceA  # Mauvais casse

# CORRECT :
spring.cloud.gateway.routes[0].uri=lb://service-a  # Minuscules, tirets

3. Service enregistré mais DOWN :

# Vérifier le statut dans Eureka
curl http://localhost:8761/eureka/apps/SERVICE-A

# Dans la réponse, chercher : "status":"DOWN"
# Si DOWN : vérifier les logs du service pour les erreurs

4. Configuration Eureka incorrecte :

# Vérifier dans application.properties du Gateway :
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/  # Bonne URL ?
eureka.client.fetch-registry=true  # true = récupère le registre
eureka.client.register-with-eureka=true  # true = s'enregistre

3 Problèmes de filtres - Diagnostic détaillé

Symptôme :

Filtres non appliqués ou erreurs de configuration

Causes possibles et solutions :

1. Syntaxe incorrecte dans .properties :

# ERREUR :
spring.cloud.gateway.routes[0].filters[0]=StripPrefix:2  # Mauvais séparateur

# CORRECT :
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=2  # Égalité =

2. Filtres personnalisés non détectés :

// ERREUR : Classe sans @Component
public class MyFilterFactory extends AbstractGatewayFilterFactory { ... }

// CORRECT : Ajouter @Component
@Component  // ← Très important !
public class MyFilterFactory extends AbstractGatewayFilterFactory { ... }

3. Ordre des filtres :

# Les filtres s'appliquent dans l'ordre des index
spring.cloud.gateway.routes[0].filters[0]=AddRequestHeader=X-First,first
spring.cloud.gateway.routes[0].filters[1]=AddRequestHeader=X-Second,second
# X-First sera ajouté avant X-Second

4. Debug des filtres :

# Activer les logs détaillés
logging.level.org.springframework.cloud.gateway=DEBUG
logging.level.org.springframework.cloud.gateway.filter=TRACE

4 Problèmes de rate limiting - Guide complet

Symptôme :

Rate limiting non fonctionnel ou erreurs Redis

Causes possibles et solutions :

1. Redis non démarré :

# Vérifier si Redis tourne
redis-cli ping
# Résultat attendu : PONG

# Démarrer Redis (si arrêté)
redis-server

# Vérifier le port
netstat -an | grep 6379

2. Configuration Redis incorrecte :

# Vérifier dans application.properties :
spring.redis.host=localhost  # Bon hôte ?
spring.redis.port=6379       # Bon port ?

# Test de connexion
telnet localhost 6379
# Doit se connecter sans erreur

3. Dépendance Redis manquante :

<!-- Vérifier dans pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

4. Configuration du rate limiter :

# Vérifier la syntaxe
# ERREUR :
spring.cloud.gateway.routes[0].filters[1]=RequestRateLimiter 10 20

# CORRECT :
spring.cloud.gateway.routes[0].filters[1]=RequestRateLimiter=10,20

Conclusion et Prochaines Étapes - Guide pour aller plus loin

1 Résumé de ce que vous avez appris - Par points

Compétences acquises :

  • Architecture : Compréhension de l'API Gateway et Eureka
  • Configuration : Maîtrise de application.properties et configuration Java
  • Routage : Routes automatiques et manuelles avec filtres
  • Sécurité : Authentification, rate limiting, CORS
  • Monitoring : Actuator, métriques, logging
  • Déploiement : Démarrage et dépannage du système

Technologies maîtrisées :

  • Spring Cloud Gateway : Routage réactif
  • Eureka Client : Découverte de services
  • Spring Security : Protection des APIs
  • Redis : Rate limiting distribué
  • Actuator : Monitoring de production

2 Prochaines étapes pour approfondir - Parcours d'apprentissage

Niveau intermédiaire :

  • Spring Cloud Security :
    • Authentification OAuth2/JWT avancée
    • Authorization avec Spring Security
    • Single Sign-On (SSO)
  • Spring Cloud Stream :
    • Communication par événements avec Kafka/RabbitMQ
    • Event-driven architecture
    • Message brokers
  • Spring Cloud Sleuth :
    • Tracing distribué avec Zipkin
    • Corrélation de logs
    • Monitoring de performance

Niveau avancé :

  • Docker Compose :
    • Déploiement conteneurisé de tous les services
    • Orchestration locale
    • Environnements de développement reproductibles
  • Kubernetes :
    • Orchestration dans un cluster de production
    • Service mesh avec Istio
    • Auto-scaling et résilience
  • Service Mesh :
    • Istio, Linkerd pour le micro-routage
    • Observabilité avancée
    • Sécurité de service à service

3 Ressources supplémentaires - Pour continuer à apprendre

Documentation officielle :

Projets GitHub à étudier :

  • Spring Cloud Samples : Exemples officiels
  • Microservices Demo : Projets complets d'architecture microservices
  • Spring Cloud Gateway Examples : Exemples de configurations avancées

Communautés et forums :

  • Stack Overflow : Tag "spring-cloud-gateway"
  • Gitter : Channels Spring Cloud
  • Reddit : r/java, r/springframework
  • Discord : Communautés Java/Spring

Livres recommandés :

  • "Spring Microservices in Action" par John Carnell
  • "Building Microservices" par Sam Newman
  • "Cloud Native Java" par Josh Long et Kenny Bastani
  • "Reactive Systems in Java" par Reactive Foundation

Félicitations ! 🎉

Vous avez maintenant une compréhension solide et pratique de Spring Cloud Gateway et de son intégration avec Eureka Server. Cette compétence est essentielle pour construire des architectures microservices modernes et évolutives.

Prochaines étapes recommandées :

  1. Pratiquez : Créez vos propres projets avec différentes configurations
  2. Expérimentez : Testez des filtres personnalisés et des routes complexes
  3. Déployez : Essayez avec Docker et Kubernetes
  4. Surveillez : Intégrez Prometheus/Grafana pour le monitoring

La maîtrise vient avec la pratique régulière. Continuez à explorer et à construire !