Eureka Server avec 3 Microservices

Qu'est-ce que Spring Cloud Eureka Server ?

Spring Cloud Eureka Server est un service de découverte (Service Discovery) qui permet aux microservices de se trouver automatiquement dans un réseau. Imaginez-le comme un annuaire téléphonique où chaque microservice s'inscrit et peut trouver les autres services.

Avantages principaux :

  • Auto-découverte : Les services se trouvent automatiquement
  • Load balancing : Distribution intelligente des charges
  • Haute disponibilité : Tolérance aux pannes
  • Scalabilité : Ajout facile de nouveaux services

Exemple

Prérequis Détaillés

1 Outils nécessaires :

  • Java JDK 11+ : Langage de programmation principal
  • Maven 3.6+ : Outil de gestion de dépendances
  • IDE : IntelliJ IDEA, Eclipse, ou VS Code
  • Postman ou curl : Pour tester les APIs

Installation étape par étape

Sur Windows :

  1. Télécharger et installer Java depuis Oracle
  2. Télécharger Apache Maven depuis le site officiel
  3. Ajouter Java et Maven au PATH système

Partie 1 : Créer le Eureka Server

1 Créer le projet Eureka Server

Utilisez Spring Initializr (https://start.spring.io) pour créer le projet :

  • Project : Maven Project
  • Dependencies : Eureka Server,web,devtools,actuator
  • Artifact : eureka-server

Fichier pom.xml du Eureka Server :


    <dependencies>
        <!-- Dépendance principale pour Eureka Server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        
        <!-- Pour exposer les endpoints web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
  

2 Configuration du Eureka Server avec .properties

Fichier application.properties du Eureka Server :

# =====================================================
# 🧭 Port du serveur Eureka
# =====================================================
# Le port sur lequel le serveur Eureka va écouter les requêtes HTTP.
# Par défaut, Eureka utilise le port 8761, mais vous pouvez le modifier
# si un autre service utilise déjà ce port.
server.port=8761


# =====================================================
# 🏷️ Nom de l'application
# =====================================================
# Ce nom est utilisé par Spring Boot pour identifier l’application.
# Il est aussi visible dans les logs et les outils de monitoring.
spring.application.name=eureka-server


# =====================================================
# ⚙️ Configuration Eureka Server
# =====================================================

# Le serveur Eureka ne doit PAS s’enregistrer lui-même.
# Cette option désactive l’inscription automatique du serveur auprès de lui-même.
# En effet, dans une architecture typique, ce sont les clients qui s’enregistrent.
eureka.client.register-with-eureka=false

# Le serveur Eureka ne doit PAS non plus chercher d’autres serveurs pour récupérer un registre.
# Cela est utile uniquement dans un cluster de plusieurs serveurs Eureka.
# Ici, comme il s’agit d’un serveur unique, on le désactive.
eureka.client.fetch-registry=false

# L’URL par défaut du registre Eureka. 
# Même si le serveur ne s’enregistre pas, cette propriété est requise
# pour certaines opérations internes (comme la gestion des endpoints).
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/


# =====================================================
# 🛑 Désactiver la protection contre les suppressions
# =====================================================
# La "self-preservation" est un mécanisme de sécurité d’Eureka
# qui empêche la suppression des instances si le serveur détecte
# une perte massive de communication avec les clients.
# Dans un environnement de développement, on peut la désactiver
# pour forcer la suppression immédiate des services inactifs.
eureka.server.enable-self-preservation=false


# =====================================================
# 🧹 Intervalle de nettoyage des services morts (en ms)
# =====================================================
# Détermine la fréquence à laquelle le serveur Eureka vérifie et
# supprime les instances non renouvelées du registre.
# Ici, toutes les 30 secondes (30000 millisecondes).
eureka.server.eviction-interval-timer-in-ms=30000


# =====================================================
# ⏱️ Durée avant qu’un service soit considéré comme mort
# =====================================================
# Si un service ne renouvelle pas son bail (heartbeat) dans ce délai,
# il est considéré comme inactif et sera supprimé du registre.
# Valeur par défaut : 90 secondes. Ici, réduite à 30 secondes pour le test.
eureka.instance.lease-expiration-duration-in-seconds=30


# =====================================================
# 🔁 Intervalle de renouvellement du bail (heartbeat)
# =====================================================
# Chaque client envoie un signal (heartbeat) pour indiquer qu’il est toujours vivant.
# Cet intervalle détermine la fréquence d’envoi de ce signal.
# Ici, toutes les 10 secondes.
eureka.instance.lease-renewal-interval-in-seconds=10


# =====================================================
# 🪵 Niveau de logging pour le débogage
# =====================================================
# Ces paramètres réduisent la verbosité des logs générés par Eureka.
# Ils peuvent être réglés à DEBUG ou TRACE pour le développement.
# Ici, désactivés pour éviter les logs inutiles.
logging.level.com.netflix.eureka=OFF
logging.level.com.netflix.discovery=OFF


# =====================================================
# 📊 Exposition des endpoints Actuator
# =====================================================
# Les endpoints Actuator permettent de surveiller et de gérer
# l’état de l’application (santé, info, env, etc.).
# Ici, on expose tous les endpoints à des fins de test.
management.endpoints.web.exposure.include=*


# =====================================================
# 🖥️ Configuration de l’instance locale
# =====================================================
# Nom d’hôte de l’instance Eureka. Généralement "localhost"
# pour un serveur local, mais peut être remplacé par une IP ou un DNS.
eureka.instance.hostname=localhost

# Si cette option est vraie, l’adresse IP de l’instance est utilisée
# au lieu du nom d’hôte pour les communications entre services.
# Cela peut éviter des erreurs de résolution DNS en environnement local.
eureka.instance.prefer-ip-address=true

Explication détaillée des propriétés :

  • server.port=8761 : Le port 8761 est conventionnel pour Eureka Server
  • eureka.client.register-with-eureka=false : Le serveur ne s'enregistre pas lui-même
  • eureka.client.fetch-registry=false : Le serveur ne récupère pas le registre
  • eureka.server.enable-self-preservation=false : Désactive la protection en développement

3 Classe principale du Eureka Server

package com.example.eurekaserver;

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

/**
 * Classe principale du Eureka Server
 * 
 * @EnableEurekaServer : Active les fonctionnalités de Eureka Server
 * Cette annotation transforme l'application en serveur de découverte
 */
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

Comprendre les annotations

  • @SpringBootApplication : Combine plusieurs annotations Spring importantes
  • @EnableEurekaServer : Transforme l'application en Eureka Server
  • SpringApplication.run() : Démarre l'application Spring Boot

4 Tester le Eureka Server

Démarrer le Eureka Server :

Accéder à l'interface web du Eureka Server :

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

Interface Eureka Server :

L'interface montre :

  • System Status : État du système
  • Instances currently registered with Eureka : Services enregistrés
  • General Info : Informations générales
  • Instance Info : Détails de l'instance

Partie 2 : Créer les Microservices Eureka Clients

1 Créer les microservices avec Eureka Client

Pour chaque microservice (A, B, C), créez un projet Spring Boot avec les dépendances :

  • Spring Web : Pour les endpoints REST
  • Eureka Discovery Client : Pour s'enregistrer auprès d'Eureka
  • Actuator : Pour le monitoring

Fichier pom.xml commun pour les microservices :


    <dependencies>
        <!-- Pour créer des APIs REST -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- Client Eureka pour s'enregistrer -->
        <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>

2 Configuration des microservices avec .properties

Fichier application.properties pour Service A :

# =====================================================
# 🌐 Port du microservice
# =====================================================
# Le port HTTP sur lequel le microservice sera lancé.
# Chaque microservice d’un système distribué doit utiliser un port différent
# pour éviter les conflits de communication.
# Exemple : service-a = 8081, service-b = 8082, etc.
server.port=8081


# =====================================================
# 🏷️ Nom de l'application
# =====================================================
# Ce nom identifie le microservice dans le registre Eureka.
# C’est ce nom (spring.application.name) que les autres services utiliseront
# pour communiquer via le mécanisme de découverte de services.
spring.application.name=service-a


# =====================================================
# 🔗 Configuration du Eureka Server
# =====================================================
# URL du serveur Eureka auprès duquel le microservice va s’enregistrer.
# Le chemin /eureka/ est obligatoire car c’est l’endpoint d’inscription.
# Le "defaultZone" indique l’adresse du serveur de découverte.
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/


# =====================================================
# 🧾 Enregistrement du microservice
# =====================================================
# Indique si ce service doit s’enregistrer auprès du serveur Eureka.
# Doit être "true" pour que le serveur puisse le rendre visible aux autres.
eureka.client.register-with-eureka=true

# Indique si ce microservice doit télécharger la liste (registre)
# de tous les autres services enregistrés auprès d’Eureka.
# Cela permet de communiquer avec d'autres services sans connaître leurs IP/ports.
eureka.client.fetch-registry=true


# =====================================================
# ⚙️ Configuration de l’instance
# =====================================================
# Utiliser l’adresse IP au lieu du nom d’hôte (utile en local ou en Docker).
# Cela évite des problèmes de résolution DNS entre conteneurs.
eureka.instance.prefer-ip-address=true

# Identifiant unique de l’instance dans Eureka.
# Combine le nom du service et le port pour différencier plusieurs instances.
eureka.instance.instance-id=${spring.application.name}:${server.port}

# Intervalle entre deux "heartbeats" (signaux de vie envoyés à Eureka).
# Ici, le microservice enverra un signal toutes les 10 secondes
# pour indiquer qu’il est encore actif.
eureka.instance.lease-renewal-interval-in-seconds=10

# Durée avant qu’Eureka considère le service comme inactif
# s’il ne reçoit plus de signaux.
# Après 30 secondes sans "heartbeat", Eureka supprime cette instance du registre.
eureka.instance.lease-expiration-duration-in-seconds=30


# =====================================================
# 💓 Statut et surveillance de l’instance
# =====================================================
# URL exposée pour consulter les informations de base de l’application.
# Utilisée par Eureka pour fournir des métadonnées sur le service.
eureka.instance.status-page-url-path=/actuator/info

# URL utilisée par Eureka pour vérifier la santé du service.
# Si cette URL retourne un statut DOWN, Eureka peut retirer temporairement
# l’instance du registre.
eureka.instance.health-check-url-path=/actuator/health


# =====================================================
# 🧩 Exposition des endpoints Actuator
# =====================================================
# Permet d’exposer les endpoints de monitoring Spring Boot Actuator.
# L’astérisque (*) indique que tous les endpoints sont accessibles
# (par exemple /health, /info, /metrics, /env, etc.).
# ⚠️ En production, il est conseillé de restreindre les endpoints exposés.
management.endpoints.web.exposure.include=*


# =====================================================
# 🧠 Configuration spécifique du microservice
# =====================================================
# Ces propriétés sont purement personnalisées.
# Elles peuvent être utilisées pour afficher des informations dans les logs
# ou pour configurer d’autres composants du service.
app.service.name=Service A
app.service.description=Premier microservice de l'application
app.service.version=1.0.0


# =====================================================
# 🪵 Niveau de Logging
# =====================================================
# Définit la verbosité des logs pour les packages Spring Cloud et Netflix (Eureka).
# Utile pour le débogage des interactions entre le client et le serveur Eureka.
# En production, il est recommandé de passer en niveau INFO ou WARN.
logging.level.org.springframework.cloud=DEBUG
logging.level.com.netflix=DEBUG

Différence entre register-with-eureka et fetch-registry

  • register-with-eureka=true : Ce service s'enregistre auprès d'Eureka
  • fetch-registry=true : Ce service récupère la liste des autres services

Fichiers similaires pour Service B et Service C avec :

  • Service B : server.port=8082 et spring.application.name=service-b
  • Service C : server.port=8083 et spring.application.name=service-c

3 Classes principales des microservices

Service A (ServiceAApplication.java) :

package com.example.servicea;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.Map;

/**
 * =====================================================
 * 🧠 ServiceAApplication.java
 * =====================================================
 * Description :
 *   - Microservice A faisant partie d'une architecture microservices Spring Cloud.
 *   - Ce service est enregistré dans le serveur Eureka (service discovery).
 *   - Il communique avec d'autres microservices (ex : Service B, Service C)
 *     en utilisant RestTemplate + Load Balancing.
 *
 * Principales annotations :
 *   @SpringBootApplication → Active Spring Boot (auto-configuration, scanning, etc.)
 *   @EnableDiscoveryClient → Active le client Eureka (inscription et découverte)
 *   @RestController → Expose les endpoints REST directement depuis cette classe
 */
@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class ServiceAApplication {

    // =====================================================
    // ⚙️ Dépendances et Beans
    // =====================================================

    /**
     * RestTemplate est utilisé pour effectuer des appels HTTP
     * entre microservices (ex : Service A → Service B).
     * L’annotation @LoadBalanced permet d’utiliser le nom logique
     * du service enregistré dans Eureka (ex : "http://service-b").
     */
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    // Injection automatique du RestTemplate
    @Autowired
    private RestTemplate restTemplate;


    // =====================================================
    // 🚀 Point d'entrée principal de l'application
    // =====================================================
    public static void main(String[] args) {
        SpringApplication.run(ServiceAApplication.class, args);
    }


    // =====================================================
    // 🌐 Endpoints REST exposés par Service A
    // =====================================================

    /**
     * Endpoint d'information sur le service.
     * Accessible via : GET http://localhost:8081/info
     */
    @GetMapping("/info")
    public Map getServiceInfo() {
        return Map.of(
            "serviceName", "Service A",
            "description", "Premier microservice de l'application",
            "port", "8081",
            "status", "UP"
        );
    }

    /**
     * Endpoint de vérification de santé.
     * Accessible via : GET http://localhost:8081/health
     * Utilisé par Eureka pour vérifier la disponibilité du service.
     */
    @GetMapping("/health")
    public String health() {
        return "Service A is running!";
    }

    /**
     * Endpoint de démonstration d'appel à un autre microservice via Eureka.
     * Utilise le RestTemplate LoadBalanced pour appeler le service-b.
     * Accessible via : GET http://localhost:8081/call-service-b
     */
    @GetMapping("/call-service-b")
    public String callServiceB() {
        try {
            // Utilisation du nom logique du service "service-b"
            // défini dans le fichier application.properties
            String response = restTemplate.getForObject("http://service-b/health", String.class);
            return "✅ Response from Service B: " + response;
        } catch (Exception e) {
            return "❌ Error calling Service B: " + e.getMessage();
        }
    }

    /**
     * Endpoint de démonstration d’appel à Service C.
     * Accessible via : GET http://localhost:8081/call-service-c
     */
    @GetMapping("/call-service-c")
    public String callServiceC() {
        try {
            String response = restTemplate.getForObject("http://service-c/health", String.class);
            return "✅ Response from Service C: " + response;
        } catch (Exception e) {
            return "❌ Error calling Service C: " + e.getMessage();
        }
    }
}

Comprendre @LoadBalanced

  • @LoadBalanced : Active le load balancing côté client
  • Utilisez http://service-name/endpoint au lieu d'URLs fixes
  • Eureka résout automatiquement le nom en adresse IP:port

4 Classes pour Service B et Service C

Service B (ServiceBApplication.java) :

package com.example.serviceb;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.Map;

@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class ServiceBApplication {
    
    @Autowired
    private RestTemplate restTemplate;

    public static void main(String[] args) {
        SpringApplication.run(ServiceBApplication.class, args);
    }
    
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
    @GetMapping("/info")
    public Map<String, Object> getServiceInfo() {
        return Map.of(
            "serviceName", "Service B",
            "description", "Service d'authentification et de gestion des utilisateurs",
            "port", "${server.port}",
            "status", "UP"
        );
    }
    
    @GetMapping("/health")
    public String health() {
        return "Service B is running!";
    }
    
    @GetMapping("/users")
    public Map<String, Object> getUsers() {
        return Map.of(
            "users", Arrays.asList("user1", "user2", "user3"),
            "count", 3
        );
    }
    
    @GetMapping("/call-service-a")
    public String callServiceA() {
        try {
            String response = restTemplate.getForObject("http://service-a/health", String.class);
            return "Response from Service A: " + response;
        } catch (Exception e) {
            return "Error calling Service A: " + e.getMessage();
        }
    }
}

Service C (ServiceCApplication.java) :

package com.example.servicec;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.Map;

@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class ServiceCApplication {
    
    @Autowired
    private RestTemplate restTemplate;

    public static void main(String[] args) {
        SpringApplication.run(ServiceCApplication.class, args);
    }
    
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
    @GetMapping("/info")
    public Map<String, Object> getServiceInfo() {
        return Map.of(
            "serviceName", "Service C",
            "description", "Service de traitement de données et d'analyse",
            "port", "${server.port}",
            "status", "UP"
        );
    }
    
    @GetMapping("/health")
    public String health() {
        return "Service C is running!";
    }
    
    @GetMapping("/data")
    public Map<String, Object> getData() {
        return Map.of(
            "data", Arrays.asList("data1", "data2", "data3"),
            "processed", true
        );
    }
    
    @GetMapping("/call-service-a")
    public String callServiceA() {
        try {
            String response = restTemplate.getForObject("http://service-a/health", String.class);
            return "Response from Service A: " + response;
        } catch (Exception e) {
            return "Error calling Service A: " + e.getMessage();
        }
    }
    
    @GetMapping("/call-service-b")
    public String callServiceB() {
        try {
            String response = restTemplate.getForObject("http://service-b/users", String.class);
            return "Response from Service B: " + response;
        } catch (Exception e) {
            return "Error calling Service B: " + e.getMessage();
        }
    }
}

Partie 3 : Tester l'Architecture Eureka

1 Démarrer l'ensemble du système

Ordre de démarrage important :

  1. Eureka Server : Doit démarrer en premier
  2. Microservices : Peuvent démarrer ensuite (dans n'importe quel ordre)

Terminal 1 : Démarrer Eureka Server

Terminal 2 : Démarrer Service A

Terminal 3 : Démarrer Service B

Terminal 4 : Démarrer Service C

2 Vérifier l'enregistrement dans Eureka

Accédez à l'interface web de Eureka :

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

Vous devriez voir quelque chose comme :

Instances currently registered with Eureka

Application	AMIs	Availability Zones	Status
SERVICE-A	n/a (1)	(1)	UP (1) - LAPTOP:service-a:8081
SERVICE-B	n/a (1)	(1)	UP (1) - LAPTOP:service-b:8082
SERVICE-C	n/a (1)	(1)	UP (1) - LAPTOP:service-c:8083

Signification des statuts :

  • UP : Service en cours d'exécution
  • DOWN : Service arrêté ou inaccessible
  • STARTING : Service en cours de démarrage

3 Tester les communications entre services

Testez les endpoints des microservices :

Tester Service A :

# Informations du service
http://localhost:8081/info

# Santé du service
http://localhost:8081/health

# Appeler Service B via Eureka
http://localhost:8081/call-service-b

# Appeler Service C via Eureka
http://localhost:8081/call-service-c

Tester Service B :

# Informations du service
http://localhost:8082/info

# Santé du service
http://localhost:8082/health

# Liste des utilisateurs
http://localhost:8082/users

# Appeler Service A via Eureka
http://localhost:8082/call-service-a

Tester Service C :

# Informations du service
http://localhost:8083/info

# Santé du service
http://localhost:8083/health

# Données traitées
http://localhost:8083/data

# Appeler Service A via Eureka
http://localhost:8083/call-service-a

# Appeler Service B via Eureka
http://localhost:8083/call-service-b

4 Simuler une panne de service

Arrêtez un service et observez le comportement :

# Arrêter Service B (Ctrl+C dans le terminal)

# Attendre 30 secondes (temps d'expiration configuré)

# Vérifier Eureka - Service B devrait disparaître
http://localhost:8761

# Essayer d'appeler Service B depuis Service A
http://localhost:8081/call-service-b
# Devrait retourner une erreur

Comportement attendu :

  • Eureka détecte automatiquement la panne
  • Les services arrêtent d'essayer de communiquer avec le service mort
  • Le load balancing évite automatiquement le service indisponible

Partie 4 : Configuration Avancée Eureka

1 Eureka Server Haute Disponibilité

Pour la production, configurez plusieurs instances de Eureka Server :

Fichier application-peer1.properties :

server.port=8761
spring.application.name=eureka-server

# Configuration peer-to-peer
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://peer2:8762/eureka/

eureka.instance.hostname=peer1
eureka.server.enable-self-preservation=false

Fichier application-peer2.properties :

server.port=8762
spring.application.name=eureka-server

# Configuration peer-to-peer
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://peer1:8761/eureka/

eureka.instance.hostname=peer2
eureka.server.enable-self-preservation=false

Démarrer les deux instances :

# Terminal 1
java -jar eureka-server.jar --spring.profiles.active=peer1

# Terminal 2
java -jar eureka-server.jar --spring.profiles.active=peer2

2 Configuration de sécurité Eureka

Ajoutez la dépendance Spring Security :

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Configuration de sécurité dans application.properties :

# Configuration de sécurité
spring.security.user.name=admin
spring.security.user.password=secure-password

# Configuration Eureka avec authentification
eureka.client.service-url.defaultZone=http://admin:secure-password@localhost:8761/eureka/

Classe de configuration de sécurité :

package com.example.eurekaserver.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeHttpRequests(authz -> authz
                .requestMatchers("/eureka/**").authenticated()
                .anyRequest().permitAll()
            )
            .httpBasic();
        return http.build();
    }
}

3 Configuration avancée des clients

Configuration complète pour les microservices :

# =====================================================
# ⚙️ Configuration Eureka Client avancée
# =====================================================

# URL du serveur Eureka auprès duquel le microservice va s’enregistrer.
# L’endpoint /eureka/ est obligatoire. C’est ici que les clients
# s’inscrivent et récupèrent les autres services.
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

# Permet à ce service de s’enregistrer automatiquement auprès du serveur Eureka.
# Doit être "true" pour que le serveur découvre ce client.
eureka.client.register-with-eureka=true

# Permet à ce client de récupérer la liste des autres services enregistrés.
# Nécessaire pour permettre la découverte dynamique et les appels interservices.
eureka.client.fetch-registry=true


# =====================================================
# ⏱️ Configuration de la fréquence d'actualisation
# =====================================================

# Définit la fréquence (en secondes) à laquelle le client rafraîchit
# la liste des services enregistrés dans Eureka.
# Ici, toutes les 30 secondes, le client mettra à jour son cache local.
# Cela permet de détecter rapidement les nouveaux services ou les arrêts.
eureka.client.registry-fetch-interval-seconds=30


# =====================================================
# 🧩 Configuration de l'instance
# =====================================================

# Utiliser l’adresse IP au lieu du nom d’hôte pour éviter
# les problèmes de résolution DNS, notamment en environnement Docker.
eureka.instance.prefer-ip-address=true

# Identifiant unique de cette instance du microservice.
# Combine le nom de l’application et le port pour assurer l’unicité.
eureka.instance.instance-id=${spring.application.name}:${server.port}

# Intervalle (en secondes) d’envoi du "heartbeat" à Eureka.
# Cela indique que l’instance est toujours active.
# Ici, toutes les 30 secondes.
eureka.instance.lease-renewal-interval-in-seconds=30

# Durée (en secondes) avant qu’Eureka considère le service comme inactif
# s’il ne reçoit plus de signaux (heartbeats).
# Ici, 90 secondes → plus tolérant aux pertes de connexion temporaires.
eureka.instance.lease-expiration-duration-in-seconds=90


# =====================================================
# 💓 URLs de statut et de santé (Spring Boot Actuator)
# =====================================================

# Page d’information (actuator/info) utilisée par Eureka pour consulter
# des métadonnées sur le service (nom, version, description, etc.)
eureka.instance.status-page-url-path=/actuator/info

# Page de santé (actuator/health) utilisée par Eureka pour vérifier
# si le service est opérationnel. Si elle renvoie "DOWN", Eureka
# peut retirer le service temporairement du registre.
eureka.instance.health-check-url-path=/actuator/health

# Page d’accueil (souvent utilisée pour renvoyer vers la documentation ou l’API principale)
eureka.instance.home-page-url-path=/


# =====================================================
# 🧭 Métadonnées personnalisées (Metadata Map)
# =====================================================

# Ces métadonnées supplémentaires sont stockées dans Eureka
# et peuvent être utilisées par d’autres services pour filtrer
# ou sélectionner des instances selon certains critères.

# Zone de déploiement logique (ex : primary, secondary, backup, etc.)
# Utile pour le routage régional ou la redondance multi-zone.
eureka.instance.metadata-map.zone=primary

# Environnement de déploiement (production, staging, test, etc.)
# Cela permet d’éviter qu’un service en développement se connecte
# accidentellement à un service de production.
eureka.instance.metadata-map.environment=production


# =====================================================
# ⏳ Timeout et gestion réseau Eureka
# =====================================================

# Délai maximal (en secondes) pour établir une connexion
# entre le client et le serveur Eureka.
# Si le serveur ne répond pas dans ce délai, la tentative échoue.
eureka.client.eureka-server-connect-timeout-seconds=5

# Délai maximal (en secondes) pour lire la réponse du serveur Eureka
# après l’établissement de la connexion.
# Ici, fixé à 8 secondes pour tolérer de légères latences réseau.
eureka.client.eureka-server-read-timeout-seconds=8

4 Load Balancing personnalisé

Configuration avancée du load balancing :

@Service
public class CustomLoadBalancer {
    
    @Autowired
    private DiscoveryClient discoveryClient;
    
    public String getNextAvailableInstance(String serviceName) {
        List<ServiceInstance> instances = discoveryClient.getInstances(serviceName);
        
        if (instances.isEmpty()) {
            throw new RuntimeException("No instances available for " + serviceName);
        }
        
        // Implémentation personnalisée de load balancing (round-robin, random, etc.)
        ServiceInstance instance = instances.get(0); // Simplifié
        return instance.getUri().toString();
    }
}

Meilleures Pratiques et Améliorations

1 Structure de configuration optimale

graph TD A[Eureka Cluster] --> B[Eureka Server 1] A --> C[Eureka Server 2] A --> D[Eureka Server 3] E[Services] --> A F[API Gateway] --> A G[Monitoring] --> A style A fill:#4CAF50 style B fill:#2196F3 style C fill:#2196F3 style D fill:#2196F3 style E fill:#FF9800 style F fill:#9C27B0 style G fill:#FF5722

2 Sécurité avancée

  • HTTPS : Utilisez HTTPS pour toutes les communications
  • Authentification : Protégez l'accès avec OAuth2 ou JWT
  • ACL : Contrôle d'accès basé sur les services
  • Monitoring : Surveillez les accès et les anomalies

Configuration de sécurité HTTPS :

############################################
# 🔧 CONFIGURATION DU SERVEUR EUREKA
############################################

# Port du serveur Eureka
server.port=8761

# Nom de l'application Eureka
spring.application.name=eureka-server

# Désactiver l'enregistrement et la récupération (car c'est un serveur)
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

# URL locale du serveur Eureka
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

# Désactivation de la protection automatique contre les suppressions accidentelles
# (utile pour le développement et les tests)
eureka.server.enable-self-preservation=false

# Intervalle de nettoyage des instances inactives (en millisecondes)
eureka.server.eviction-interval-timer-in-ms=30000

# Délai avant qu’un service soit considéré comme mort (en secondes)
eureka.instance.lease-expiration-duration-in-seconds=30

# Intervalle de renouvellement du bail (en secondes)
eureka.instance.lease-renewal-interval-in-seconds=10

# Exposition de tous les endpoints Actuator
management.endpoints.web.exposure.include=*

# Configuration de l’instance du serveur
eureka.instance.hostname=localhost
eureka.instance.prefer-ip-address=true

# Niveau de logs pour le serveur Eureka
logging.level.com.netflix.eureka=OFF
logging.level.com.netflix.discovery=OFF

############################################
# ⚙️ CONFIGURATION DU MICRO SERVICE (CLIENT)
############################################

# Port du microservice
server.port=8081

# Nom du microservice (utilisé pour l'enregistrement Eureka)
spring.application.name=service-a

# URL du serveur Eureka
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

# Enregistrement du microservice dans Eureka
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true

# Identifiant unique de l’instance
eureka.instance.instance-id=${spring.application.name}:${server.port}

# Préférence pour utiliser l’adresse IP au lieu du nom d’hôte
eureka.instance.prefer-ip-address=true

# Intervalle de renouvellement et d’expiration du bail
eureka.instance.lease-renewal-interval-in-seconds=10
eureka.instance.lease-expiration-duration-in-seconds=30

# URLs de statut et de santé du microservice
eureka.instance.status-page-url-path=/actuator/info
eureka.instance.health-check-url-path=/actuator/health

# Exposition de tous les endpoints Actuator
management.endpoints.web.exposure.include=*

# Informations supplémentaires sur le service
app.service.name=Service A
app.service.description=Premier microservice de l'application
app.service.version=1.0.0

# Niveau de logs du client Eureka
logging.level.org.springframework.cloud=DEBUG
logging.level.com.netflix=DEBUG

############################################
# 🚀 CONFIGURATION EUREKA CLIENT AVANCÉE
############################################

# URL du serveur Eureka (répétée pour compatibilité avancée)
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

# Activation de l'enregistrement et de la récupération
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true

# Fréquence d’actualisation du registre (en secondes)
eureka.client.registry-fetch-interval-seconds=30

# Métadonnées pour classification (utile dans des environnements multi-zones)
eureka.instance.metadata-map.zone=primary
eureka.instance.metadata-map.environment=production

# Timeout pour la connexion au serveur Eureka
eureka.client.eureka-server-connect-timeout-seconds=5
eureka.client.eureka-server-read-timeout-seconds=8

############################################
# 🔒 CONFIGURATION HTTPS DU MICRO SERVICE
############################################

# Définition du keystore (certificat SSL)
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=password
server.ssl.keyStoreType=PKCS12
server.ssl.keyAlias=tomcat

# Activation du port sécurisé pour Eureka
eureka.instance.secure-port-enabled=true
eureka.instance.non-secure-port-enabled=false
eureka.instance.secure-port=${server.port}
eureka.instance.secure-virtual-host-name=${spring.application.name}

3 Performance et monitoring

  • Caching : Activez le cache des registres
  • Compression : Utilisez la compression GZIP
  • Timeouts : Configurez des timeouts appropriés
  • Monitoring : Surveillez avec Actuator et Prometheus

Configuration de performance :

# Configuration de performance Eureka
eureka.server.response-cache-update-interval-ms=30000
eureka.server.response-cache-auto-expiration-in-seconds=180
eureka.server.use-read-only-response-cache=true

# Compression
server.compression.enabled=true
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain

# Monitoring avec Actuator
management.endpoints.web.exposure.include=health,info,metrics,eureka
management.endpoint.health.show-details=always
management.metrics.export.prometheus.enabled=true

4 Gestion des erreurs et fallback

Configuration robuste avec fallback :

# Configuration resilience
eureka.client.initial-instance-info-replication-interval-seconds=40
eureka.client.instance-info-replication-interval-seconds=30
eureka.client.eureka-service-url-poll-interval-seconds=300

# Fallback configuration
spring.cloud.loadbalancer.retry.enabled=true
spring.cloud.loadbalancer.retry.max-attempts=3
spring.cloud.loadbalancer.retry.backoff.initial-interval=1000
spring.cloud.loadbalancer.retry.backoff.multiplier=2.0
spring.cloud.loadbalancer.retry.backoff.max-interval=10000

Dépannage

1 Services non enregistrés dans Eureka

Symptôme : Services UP mais absents de l'interface Eureka

Cause possible : Configuration incorrecte de l'URL Eureka

Solution :

  1. Vérifiez eureka.client.service-url.defaultZone dans application.properties
  2. Assurez-vous que le Eureka Server est démarré
  3. Vérifiez que eureka.client.register-with-eureka=true
  4. Consultez les logs du microservice

2 Erreurs de communication entre services

Symptôme : "UnknownHostException" ou "Connection refused"

Cause possible : Nom de service incorrect ou service indisponible

Solution :

  1. Vérifiez que vous utilisez le bon nom de service (spring.application.name)
  2. Assurez-vous que le service cible est démarré et enregistré
  3. Vérifiez que @LoadBalanced est présent sur RestTemplate
  4. Testez avec http://localhost:8761/eureka/apps

3 Services marqués comme DOWN

Symptôme : Services affichés en rouge (DOWN) dans Eureka

Cause possible : Endpoint de santé inaccessible ou en échec

Solution :

  1. Vérifiez que l'endpoint /actuator/health répond correctement
  2. Assurez-vous que le port est accessible
  3. Vérifiez les logs du service pour les erreurs
  4. Augmentez eureka.instance.lease-expiration-duration-in-seconds

4 Problèmes de load balancing

Symptôme : Toujours le même service appelé ou erreurs aléatoires

Cause possible : Configuration manquante de @LoadBalanced

Solution :

  1. Vérifiez que RestTemplate est annoté avec @LoadBalanced
  2. Assurez-vous d'utiliser le nom logique du service
  3. Vérifiez la configuration des timeouts
  4. Testez avec plusieurs instances du même service

Conclusion et Prochaines Étapes

1 Résumé de ce que vous avez appris

Dans ce tutoriel complet, vous avez appris à :

  • Implémenter un Spring Cloud Eureka Server pour la découverte de services
  • Créer trois microservices qui s'enregistrent automatiquement
  • Utiliser le load balancing client avec @LoadBalanced
  • Communiquer entre services via leurs noms logiques
  • Appliquer les meilleures pratiques de sécurité et de performance
  • Dépanner les problèmes courants de découverte de services

2 Prochaines étapes pour approfondir

  • Spring Cloud Gateway : API Gateway pour router les requêtes
  • Spring Cloud Config Server : Gestion centralisée des configurations
  • Spring Cloud Circuit Breaker : Résilience avec Hystrix ou Resilience4j
  • Docker : Conteneurisation de vos microservices
  • Kubernetes : Orchestration des conteneurs