Hystrix

Tolérance aux pannes avec Circuit Breaker (Déprécié - )

Introduction à Hystrix

Qu'est-ce que Hystrix ?

⚠️ ATTENTION - PRODUIT DÉPRÉCIÉ

Hystrix est officiellement déprécié depuis novembre 2018. Cette documentation est fournie à titre historique et éducatif. Pour les nouveaux projets, utilisez Resilience4j à la place.

Hystrix était une bibliothèque de tolérance aux pannes développée par Netflix. Elle permettait de contrôler les points de latence et d'échec entre les services distribués, empêchant les cascades d'échecs et permettant de donner aux services le temps de récupérer.

graph LR A[Client
Service A] --> B[Hystrix] B --> C[Circuit Breaker] C --> D[Service B] C --> E[Fallback] style A fill:#FF9800,stroke:#E65100 style B fill:#4CAF50,stroke:#388E3C style C fill:#2196F3,stroke:#0D47A1 style D fill:#9C27B0,stroke:#4A148C style E fill:#FF5722,stroke:#E64A19

Fonctionnalités principales de Hystrix :

  • Circuit Breaker : Empêche les appels répétés vers un service défaillant
  • Fallback : Fournit des réponses alternatives en cas de panne
  • Timeouts : Limite la durée d'attente pour les appels
  • Monitoring : Dashboard en temps réel des performances
  • Isolation : Isole les appels pour éviter la propagation des pannes
  • Métriques : Collecte détaillée des statistiques

Architecture avec Hystrix

Structure de l'application

graph TD A[Eureka Server
Port: 8761] --> B[Service A
Client - Port: 8080] A --> C[Service B
Users - Port: 8081] B -- Hystrix --> D[Hystrix Dashboard
Port: 9000] B -- Hystrix Commands --> C style A fill:#2196F3,stroke:#0D47A1 style B fill:#FF9800,stroke:#E65100 style C fill:#4CAF50,stroke:#388E3C style D fill:#9C27B0,stroke:#4A148C

Rôles des composants :

  • Service A : Client qui utilise Hystrix pour communiquer
  • Service B : Service distant pouvant tomber en panne
  • Eureka Server : Serveur de découverte de services
  • Hystrix Dashboard : Interface de monitoring en temps réel
  • Hystrix Commands : Commandes encapsulant les appels distants

Service B - Service Utilisateurs

1 Structure du projet

Arborescence du projet :

serviceB/
├── pom.xml
└── src/main/
    ├── java/com/example/users/
    │   ├── UsersMicroserviceApplication.java
    │   ├── controller/
    │   │   └── UserController.java
    │   ├── model/
    │   │   └── User.java
    └── resources/
        └── application.properties

2 Fichier pom.xml

Dépendances Maven :

serviceB/pom.xml
<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Eureka Client -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
    <!-- Actuator pour monitoring -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>

3 Configuration application.properties

Fichier de configuration :

serviceB/src/main/resources/application.properties
# === CONFIGURATION SERVEUR ===
server.port=8081
spring.application.name=service-b

# === CONFIGURATION EUREKA ===
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
eureka.instance.prefer-ip-address=true

# === CONFIGURATION ACTUATOR ===
management.endpoints.web.exposure.include=health,info,metrics
management.endpoint.health.show-details=always

4 Modèle User

Modèle User :

serviceB/src/main/java/com/example/users/model/User.java
package com.example.users.model;

/**
 * Modèle représentant un utilisateur
 */
public class User {
    private Long id;
    private String name;
    private String email;
    private String department;
    
    // Constructeurs

    
    // Getters et Setters
    

5 Contrôleur REST

Contrôleur REST :

serviceB/src/main/java/com/example/users/controller/UserController.java
package com.example.users.controller;

import com.example.users.model.User;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Contrôleur REST pour les opérations sur les utilisateurs
 */
@RestController
@RequestMapping("/api/users")
@CrossOrigin(origins = "*")
public class UserController {
    
    // Stockage en mémoire pour la démonstration
    private final ConcurrentHashMap<Long, User> users = new ConcurrentHashMap<>();
    private final AtomicLong counter = new AtomicLong();
    
    // Initialisation avec quelques utilisateurs
    public UserController() {
        users.put(counter.incrementAndGet(), new User(1L, "John Doe", "john@example.com", "IT"));
        users.put(counter.incrementAndGet(), new User(2L, "Jane Smith", "jane@example.com", "HR"));
        users.put(counter.incrementAndGet(), new User(3L, "Bob Johnson", "bob@example.com", "Finance"));
        counter.set(3);
    }
    
    /**
     * Récupère tous les utilisateurs
     * @return Liste d'utilisateurs
     */
    @GetMapping
    public List<User> getAllUsers() {
        return new ArrayList<>(users.values());
    }
    
    /**
     * Récupère un utilisateur par son ID
     * @param id ID de l'utilisateur
     * @return Utilisateur trouvé
     */
    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return users.get(id);
    }
    
    /**
     * Crée un nouvel utilisateur
     * @param user Utilisateur à créer
     * @return Utilisateur créé
     */
    @PostMapping
    public User createUser(@RequestBody User user) {
        Long id = counter.incrementAndGet();
        user.setId(id);
        users.put(id, user);
        return user;
    }
    
    /**
     * Met à jour un utilisateur existant
     * @param id ID de l'utilisateur
     * @param user Données de mise à jour
     * @return Utilisateur mis à jour
     */
    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        user.setId(id);
        users.put(id, user);
        return user;
    }
    
    /**
     * Supprime un utilisateur
     * @param id ID de l'utilisateur à supprimer
     */
    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        users.remove(id);
    }
    
    /**
     * Recherche par département
     */
    @GetMapping(params = "department")
    public List<User> getUsersByDepartment(@RequestParam String department) {
        List<User> result = new ArrayList<>();
        for (User user : users.values()) {
            if (user.getDepartment().equals(department)) {
                result.add(user);
            }
        }
        return result;
    }
    
    /**
     * Endpoint qui simule une erreur pour tester Hystrix
     */
    @GetMapping("/error")
    public User getErrorUser() {
        // Simule une erreur 50% du temps
        if (Math.random() > 0.5) {
            throw new RuntimeException("Erreur simulée pour tester Hystrix");
        }
        return new User(999L, "Error Test", "error@test.com", "TEST");
    }
    
    /**
     * Endpoint qui simule un timeout pour tester Hystrix
     */
    @GetMapping("/slow")
    public User getSlowUser() throws InterruptedException {
        // Simule un traitement lent
        Thread.sleep(3000); // 3 secondes
        return new User(888L, "Slow Test", "slow@test.com", "TEST");
    }
}

6 Classe principale

Application Spring Boot :

serviceB/src/main/java/com/example/users/UsersMicroserviceApplication.java
package com.example.users;

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

/**
 * Classe principale du microservice de gestion des utilisateurs
 */
@SpringBootApplication
@EnableDiscoveryClient
public class UsersMicroserviceApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(UsersMicroserviceApplication.class, args);
    }
}

Service A - Client avec Hystrix

1 Configuration de Base de Hystrix

⚠️ RAPPEL - Hystrix est déprécié

Pour les nouveaux projets, utilisez Resilience4j à la place.

Dépendances Maven :

serviceA/pom.xml
<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Eureka Client -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
    <!-- Hystrix Core -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    
    <!-- Hystrix Dashboard -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
    </dependency>
    
    <!-- Actuator pour monitoring -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    
    <!-- RestTemplate -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- OpenFeign -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
</dependencies>

Activation des fonctionnalités :

serviceA/src/main/java/com/example/client/ClientMicroserviceApplication.java
package com.example.client;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * Classe principale du microservice client avec Hystrix
 * 
 * @EnableCircuitBreaker - Active Hystrix Circuit Breaker
 * @EnableHystrixDashboard - Active le dashboard Hystrix
 */
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker  // Active Hystrix
@EnableHystrixDashboard // Active le dashboard Hystrix
@EnableFeignClients     // Active OpenFeign
public class ClientMicroserviceApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(ClientMicroserviceApplication.class, args);
    }
}

2 Modèle de Données

Modèle User :

serviceA/src/main/java/com/example/client/model/User.java
package com.example.client.model;

/**
 * Modèle représentant un utilisateur
 */
public class User {
    private Long id;
    private String name;
    private String email;
    private String department;
    
    // Constructeurs
   
    // Getters et Setters
   

3 Hystrix Commands avec RestTemplate

Commandes Hystrix :

serviceA/src/main/java/com/example/client/hystrix/GetUserCommand.java
package com.example.client.hystrix;

import com.example.client.model.User;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import org.springframework.web.client.RestTemplate;

/**
 * Commande Hystrix pour récupérer un utilisateur
 * 
 * HystrixCommand - Classe de base pour les commandes Hystrix
 * Permet de gérer le circuit breaker, les timeouts et les fallbacks
 */
public class GetUserCommand extends HystrixCommand<User> {
    
    private final RestTemplate restTemplate;
    private final Long userId;
    private final String serviceUrl;
    
    public GetUserCommand(RestTemplate restTemplate, Long userId, String serviceUrl) {
        super(Setter
                .withGroupKey(HystrixCommandGroupKey.Factory.asKey("UserServiceGroup"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("GetUserCommand"))
                .andCommandPropertiesDefaults(
                        HystrixCommandProperties.Setter()
                                .withExecutionTimeoutInMilliseconds(2000)  // Timeout de 2 secondes
                                .withCircuitBreakerRequestVolumeThreshold(5)  // 5 requêtes minimum
                                .withCircuitBreakerErrorThresholdPercentage(50)  // 50% d'erreurs
                                .withCircuitBreakerSleepWindowInMilliseconds(5000)  // 5 secondes d'attente
                )
        );
        this.restTemplate = restTemplate;
        this.userId = userId;
        this.serviceUrl = serviceUrl;
    }
    
    /**
     * Méthode principale - exécutée dans un thread séparé
     * Contient la logique d'appel distant
     */
    @Override
    protected User run() throws Exception {
        String url = serviceUrl + "/api/users/" + userId;
        return restTemplate.getForObject(url, User.class);
    }
    
    /**
     * Méthode de fallback - appelée en cas d'erreur, timeout ou circuit breaker ouvert
     */
    @Override
    protected User getFallback() {
        System.err.println("Fallback appelé pour GetUserCommand - userId: " + userId);
        
        // Retourne un utilisateur par défaut
        return new User(userId, "Utilisateur par défaut", "default@example.com", "DEFAULT");
    }
}
serviceA/src/main/java/com/example/client/hystrix/GetAllUsersCommand.java
package com.example.client.hystrix;

import com.example.client.model.User;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;
import java.util.List;

/**
 * Commande Hystrix pour récupérer tous les utilisateurs
 */
public class GetAllUsersCommand extends HystrixCommand<List<User>> {
    
    private final RestTemplate restTemplate;
    private final String serviceUrl;
    
    public GetAllUsersCommand(RestTemplate restTemplate, String serviceUrl) {
        super(Setter
                .withGroupKey(HystrixCommandGroupKey.Factory.asKey("UserServiceGroup"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("GetAllUsersCommand"))
                .andCommandPropertiesDefaults(
                        HystrixCommandProperties.Setter()
                                .withExecutionTimeoutInMilliseconds(3000)  // Timeout de 3 secondes
                                .withCircuitBreakerRequestVolumeThreshold(5)
                                .withCircuitBreakerErrorThresholdPercentage(50)
                                .withCircuitBreakerSleepWindowInMilliseconds(5000)
                )
        );
        this.restTemplate = restTemplate;
        this.serviceUrl = serviceUrl;
    }
    
    @Override
    protected List<User> run() throws Exception {
        String url = serviceUrl + "/api/users";
        User[] usersArray = restTemplate.getForObject(url, User[].class);
        return Arrays.asList(usersArray);
    }
    
    @Override
    protected List<User> getFallback() {
        System.err.println("Fallback appelé pour GetAllUsersCommand");
        
        // Retourne une liste vide ou une liste par défaut
        return Arrays.asList(
            new User(0L, "Utilisateur par défaut", "default@example.com", "DEFAULT")
        );
    }
}
serviceA/src/main/java/com/example/client/hystrix/CreateUserCommand.java
package com.example.client.hystrix;

import com.example.client.model.User;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import org.springframework.web.client.RestTemplate;

/**
 * Commande Hystrix pour créer un utilisateur
 */
public class CreateUserCommand extends HystrixCommand<User> {
    
    private final RestTemplate restTemplate;
    private final User user;
    private final String serviceUrl;
    
    public CreateUserCommand(RestTemplate restTemplate, User user, String serviceUrl) {
        super(Setter
                .withGroupKey(HystrixCommandGroupKey.Factory.asKey("UserServiceGroup"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("CreateUserCommand"))
                .andCommandPropertiesDefaults(
                        HystrixCommandProperties.Setter()
                                .withExecutionTimeoutInMilliseconds(2000)
                                .withCircuitBreakerRequestVolumeThreshold(5)
                                .withCircuitBreakerErrorThresholdPercentage(50)
                                .withCircuitBreakerSleepWindowInMilliseconds(5000)
                )
        );
        this.restTemplate = restTemplate;
        this.user = user;
        this.serviceUrl = serviceUrl;
    }
    
    @Override
    protected User run() throws Exception {
        String url = serviceUrl + "/api/users";
        return restTemplate.postForObject(url, user, User.class);
    }
    
    @Override
    protected User getFallback() {
        System.err.println("Fallback appelé pour CreateUserCommand");
        
        // Retourne l'utilisateur avec un ID par défaut
        User fallbackUser = new User(0L, user.getName(), user.getEmail(), user.getDepartment());
        return fallbackUser;
    }
}

4 Services utilisant Hystrix Commands

Service avec RestTemplate et Hystrix :

serviceA/src/main/java/com/example/client/config/HystrixConfig.java
package com.example.client.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * Configuration de Hystrix et RestTemplate
 */
@Configuration
public class HystrixConfig {
    
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
serviceA/src/main/java/com/example/client/service/HystrixRestTemplateService.java
package com.example.client.service;

import com.example.client.hystrix.*;
import com.example.client.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
 * Service utilisant Hystrix Commands avec RestTemplate
 */
@Service
public class HystrixRestTemplateService {
    
    @Autowired
    private RestTemplate restTemplate;
    
    private static final String SERVICE_URL = "http://service-b";
    
    /**
     * Récupère un utilisateur avec Hystrix Command
     */
    public User getUserById(Long id) {
        GetUserCommand command = new GetUserCommand(restTemplate, id, SERVICE_URL);
        return command.execute(); // Exécute la commande Hystrix
    }
    
    /**
     * Récupère tous les utilisateurs avec Hystrix Command
     */
    public List<User> getAllUsers() {
        GetAllUsersCommand command = new GetAllUsersCommand(restTemplate, SERVICE_URL);
        return command.execute();
    }
    
    /**
     * Crée un utilisateur avec Hystrix Command
     */
    public User createUser(User user) {
        CreateUserCommand command = new CreateUserCommand(restTemplate, user, SERVICE_URL);
        return command.execute();
    }
    
    /**
     * Mise à jour d'utilisateur avec Hystrix Command
     */
    public User updateUser(Long id, User user) {
        UpdateUserCommand command = new UpdateUserCommand(restTemplate, id, user, SERVICE_URL);
        return command.execute();
    }
    
    /**
     * Suppression d'utilisateur avec Hystrix Command
     */
    public void deleteUser(Long id) {
        DeleteUserCommand command = new DeleteUserCommand(restTemplate, id, SERVICE_URL);
        command.execute();
    }
    
    // === TESTS DE TOLÉRANCE AUX PANNES ===
    
    public User getErrorUser() {
        GetErrorUserCommand command = new GetErrorUserCommand(restTemplate, SERVICE_URL);
        return command.execute();
    }
    
    public User getSlowUser() {
        GetSlowUserCommand command = new GetSlowUserCommand(restTemplate, SERVICE_URL);
        return command.execute();
    }
}
serviceA/src/main/java/com/example/client/hystrix/UpdateUserCommand.java
package com.example.client.hystrix;

import com.example.client.model.User;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import org.springframework.web.client.RestTemplate;

/**
 * Commande Hystrix pour mettre à jour un utilisateur
 */
public class UpdateUserCommand extends HystrixCommand<User> {
    
    private final RestTemplate restTemplate;
    private final Long userId;
    private final User user;
    private final String serviceUrl;
    
    public UpdateUserCommand(RestTemplate restTemplate, Long userId, User user, String serviceUrl) {
        super(Setter
                .withGroupKey(HystrixCommandGroupKey.Factory.asKey("UserServiceGroup"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("UpdateUserCommand"))
                .andCommandPropertiesDefaults(
                        HystrixCommandProperties.Setter()
                                .withExecutionTimeoutInMilliseconds(2000)
                                .withCircuitBreakerRequestVolumeThreshold(5)
                                .withCircuitBreakerErrorThresholdPercentage(50)
                                .withCircuitBreakerSleepWindowInMilliseconds(5000)
                )
        );
        this.restTemplate = restTemplate;
        this.userId = userId;
        this.user = user;
        this.serviceUrl = serviceUrl;
    }
    
    @Override
    protected User run() throws Exception {
        String url = serviceUrl + "/api/users/" + userId;
        restTemplate.put(url, user);
        return user; // Retourne l'utilisateur mis à jour
    }
    
    @Override
    protected User getFallback() {
        System.err.println("Fallback appelé pour UpdateUserCommand - userId: " + userId);
        return new User(userId, user.getName(), user.getEmail(), user.getDepartment());
    }
}
serviceA/src/main/java/com/example/client/hystrix/DeleteUserCommand.java
package com.example.client.hystrix;

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import org.springframework.web.client.RestTemplate;

/**
 * Commande Hystrix pour supprimer un utilisateur
 */
public class DeleteUserCommand extends HystrixCommand<Void> {
    
    private final RestTemplate restTemplate;
    private final Long userId;
    private final String serviceUrl;
    
    public DeleteUserCommand(RestTemplate restTemplate, Long userId, String serviceUrl) {
        super(Setter
                .withGroupKey(HystrixCommandGroupKey.Factory.asKey("UserServiceGroup"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("DeleteUserCommand"))
                .andCommandPropertiesDefaults(
                        HystrixCommandProperties.Setter()
                                .withExecutionTimeoutInMilliseconds(2000)
                                .withCircuitBreakerRequestVolumeThreshold(5)
                                .withCircuitBreakerErrorThresholdPercentage(50)
                                .withCircuitBreakerSleepWindowInMilliseconds(5000)
                )
        );
        this.restTemplate = restTemplate;
        this.userId = userId;
        this.serviceUrl = serviceUrl;
    }
    
    @Override
    protected Void run() throws Exception {
        String url = serviceUrl + "/api/users/" + userId;
        restTemplate.delete(url);
        return null;
    }
    
    @Override
    protected Void getFallback() {
        System.err.println("Fallback appelé pour DeleteUserCommand - userId: " + userId);
        return null;
    }
}

5 Hystrix avec OpenFeign

Client Feign avec Hystrix :

serviceA/src/main/java/com/example/client/feign/UserServiceClient.java
package com.example.client.feign;

import com.example.client.model.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * Client Feign pour le service utilisateurs avec Hystrix
 * 
 * fallback - Classe de fallback implémentant l'interface
 */
@FeignClient(
    name = "service-b",
    fallback = UserServiceClientFallback.class  // Fallback pour Hystrix
)
public interface UserServiceClient {
    
    @GetMapping("/api/users")
    List<User> getAllUsers();
    
    @GetMapping("/api/users/{id}")
    User getUserById(@PathVariable("id") Long id);
    
    @PostMapping("/api/users")
    User createUser(@RequestBody User user);
    
    @PutMapping("/api/users/{id}")
    User updateUser(@PathVariable("id") Long id, @RequestBody User user);
    
    @DeleteMapping("/api/users/{id}")
    void deleteUser(@PathVariable("id") Long id);
    
    @GetMapping("/api/users")
    List<User> getUsersByDepartment(@RequestParam("department") String department);
    
    // Endpoints pour tests Hystrix
    @GetMapping("/api/users/error")
    User getErrorUser();
    
    @GetMapping("/api/users/slow")
    User getSlowUser();
}
serviceA/src/main/java/com/example/client/feign/UserServiceClientFallback.java
package com.example.client.feign;

import com.example.client.model.User;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.List;

/**
 * Fallback pour le client Feign UserService
 * Implémente l'interface avec des réponses par défaut
 */
@Component
public class UserServiceClientFallback implements UserServiceClient {
    
    @Override
    public List<User> getAllUsers() {
        System.err.println("Fallback: getAllUsers");
        return Collections.singletonList(
            new User(0L, "Utilisateur par défaut", "default@example.com", "DEFAULT")
        );
    }
    
    @Override
    public User getUserById(Long id) {
        System.err.println("Fallback: getUserById - " + id);
        return new User(id, "Utilisateur par défaut", "default@example.com", "DEFAULT");
    }
    
    @Override
    public User createUser(User user) {
        System.err.println("Fallback: createUser");
        return new User(0L, user.getName(), user.getEmail(), user.getDepartment());
    }
    
    @Override
    public User updateUser(Long id, User user) {
        System.err.println("Fallback: updateUser - " + id);
        return new User(id, user.getName(), user.getEmail(), user.getDepartment());
    }
    
    @Override
    public void deleteUser(Long id) {
        System.err.println("Fallback: deleteUser - " + id);
    }
    
    @Override
    public List<User> getUsersByDepartment(String department) {
        System.err.println("Fallback: getUsersByDepartment - " + department);
        return Collections.singletonList(
            new User(0L, "Utilisateur par défaut", "default@example.com", "DEFAULT")
        );
    }
    
    @Override
    public User getErrorUser() {
        System.err.println("Fallback: getErrorUser");
        return new User(999L, "Utilisateur d'erreur", "error@example.com", "ERROR");
    }
    
    @Override
    public User getSlowUser() {
        System.err.println("Fallback: getSlowUser");
        return new User(888L, "Utilisateur lent", "slow@example.com", "SLOW");
    }
}

6 Configuration de Hystrix

Configuration application.properties :

serviceA/src/main/resources/application.properties
# === CONFIGURATION SERVEUR ===
server.port=8080
spring.application.name=client-service

# === CONFIGURATION EUREKA ===
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
eureka.instance.prefer-ip-address=true

# === CONFIGURATION HYSTRIX ===

# Activation de Hystrix
feign.hystrix.enabled=true

# Configuration globale de Hystrix
hystrix.command.default.execution.timeout.enabled=true
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000

# Configuration du circuit breaker
hystrix.command.default.circuitBreaker.requestVolumeThreshold=5
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000

# Configuration du thread pool
hystrix.threadpool.default.coreSize=10
hystrix.threadpool.default.maximumSize=20
hystrix.threadpool.default.maxQueueSize=100
hystrix.threadpool.default.queueSizeRejectionThreshold=80

# === CONFIGURATION DES COMMANDES SPÉCIFIQUES ===

# GetUserCommand
hystrix.command.GetUserCommand.execution.isolation.thread.timeoutInMilliseconds=2000
hystrix.command.GetUserCommand.circuitBreaker.requestVolumeThreshold=5

# GetAllUsersCommand
hystrix.command.GetAllUsersCommand.execution.isolation.thread.timeoutInMilliseconds=3000
hystrix.command.GetAllUsersCommand.circuitBreaker.requestVolumeThreshold=5

# CreateUserCommand
hystrix.command.CreateUserCommand.execution.isolation.thread.timeoutInMilliseconds=2000
hystrix.command.CreateUserCommand.circuitBreaker.requestVolumeThreshold=5

# === CONFIGURATION ACTUATOR ===
management.endpoints.web.exposure.include=health,info,metrics,hystrix.stream
management.endpoint.health.show-details=always

# === CONFIGURATION DU DASHBOARD HYSTRIX ===
hystrix.dashboard.proxy-stream-allow-list=*

7 Services Métier

Services utilisant Hystrix :

serviceA/src/main/java/com/example/client/service/UserService.java
package com.example.client.service;

import com.example.client.feign.UserServiceClient;
import com.example.client.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * Service métier pour les utilisateurs avec Hystrix
 */
@Service
public class UserService {
    
    @Autowired
    private HystrixRestTemplateService hystrixRestTemplateService;
    
    @Autowired
    private UserServiceClient userServiceClient;
    
    // === MÉTHODES AVEC HYSTRIX COMMANDS (RestTemplate) ===
    
    public User getUserByIdHystrix(Long id) {
        return hystrixRestTemplateService.getUserById(id);
    }
    
    public List<User> getAllUsersHystrix() {
        return hystrixRestTemplateService.getAllUsers();
    }
    
    public User createUserHystrix(User user) {
        return hystrixRestTemplateService.createUser(user);
    }
    
    public User updateUserHystrix(Long id, User user) {
        return hystrixRestTemplateService.updateUser(id, user);
    }
    
    public void deleteUserHystrix(Long id) {
        hystrixRestTemplateService.deleteUser(id);
    }
    
    public User getErrorUserHystrix() {
        return hystrixRestTemplateService.getErrorUser();
    }
    
    public User getSlowUserHystrix() {
        return hystrixRestTemplateService.getSlowUser();
    }
    
    // === MÉTHODES AVEC FEIGN ET HYSTRIX ===
    
    public List<User> getAllUsersFeign() {
        return userServiceClient.getAllUsers();
    }
    
    public User getUserByIdFeign(Long id) {
        return userServiceClient.getUserById(id);
    }
    
    public User createUserFeign(User user) {
        return userServiceClient.createUser(user);
    }
    
    public User updateUserFeign(Long id, User user) {
        return userServiceClient.updateUser(id, user);
    }
    
    public void deleteUserFeign(Long id) {
        userServiceClient.deleteUser(id);
    }
    
    public List<User> getUsersByDepartmentFeign(String department) {
        return userServiceClient.getUsersByDepartment(department);
    }
    
    public User getErrorUserFeign() {
        return userServiceClient.getErrorUser();
    }
    
    public User getSlowUserFeign() {
        return userServiceClient.getSlowUser();
    }
}

8 Contrôleur REST Client

Contrôleur exposant les fonctionnalités :

serviceA/src/main/java/com/example/client/controller/HystrixController.java
package com.example.client.controller;

import com.example.client.model.User;
import com.example.client.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * Contrôleur REST pour exposer les fonctionnalités Hystrix
 */
@RestController
@RequestMapping("/api/hystrix")
@CrossOrigin(origins = "*")
public class HystrixController {
    
    @Autowired
    private UserService userService;
    
    // === ENDPOINTS AVEC HYSTRIX COMMANDS ===
    
    @GetMapping("/users/hystrix/{id}")
    public User getUserByIdHystrix(@PathVariable Long id) {
        return userService.getUserByIdHystrix(id);
    }
    
    @GetMapping("/users/hystrix")
    public List<User> getAllUsersHystrix() {
        return userService.getAllUsersHystrix();
    }
    
    @PostMapping("/users/hystrix")
    public User createUserHystrix(@RequestBody User user) {
        return userService.createUserHystrix(user);
    }
    
    @PutMapping("/users/hystrix/{id}")
    public User updateUserHystrix(@PathVariable Long id, @RequestBody User user) {
        return userService.updateUserHystrix(id, user);
    }
    
    @DeleteMapping("/users/hystrix/{id}")
    public ResponseEntity<Void> deleteUserHystrix(@PathVariable Long id) {
        userService.deleteUserHystrix(id);
        return ResponseEntity.ok().build();
    }
    
    @GetMapping("/users/hystrix/error")
    public User getErrorUserHystrix() {
        return userService.getErrorUserHystrix();
    }
    
    @GetMapping("/users/hystrix/slow")
    public User getSlowUserHystrix() {
        return userService.getSlowUserHystrix();
    }
    
    // === ENDPOINTS AVEC FEIGN ET HYSTRIX ===
    
    @GetMapping("/users/feign")
    public List<User> getAllUsersFeign() {
        return userService.getAllUsersFeign();
    }
    
    @GetMapping("/users/feign/{id}")
    public User getUserByIdFeign(@PathVariable Long id) {
        return userService.getUserByIdFeign(id);
    }
    
    @PostMapping("/users/feign")
    public User createUserFeign(@RequestBody User user) {
        return userService.createUserFeign(user);
    }
    
    @PutMapping("/users/feign/{id}")
    public User updateUserFeign(@PathVariable Long id, @RequestBody User user) {
        return userService.updateUserFeign(id, user);
    }
    
    @DeleteMapping("/users/feign/{id}")
    public ResponseEntity<Void> deleteUserFeign(@PathVariable Long id) {
        userService.deleteUserFeign(id);
        return ResponseEntity.ok().build();
    }
    
    @GetMapping("/users/feign/department/{department}")
    public List<User> getUsersByDepartmentFeign(@PathVariable String department) {
        return userService.getUsersByDepartmentFeign(department);
    }
    
    @GetMapping("/users/feign/error")
    public User getErrorUserFeign() {
        return userService.getErrorUserFeign();
    }
    
    @GetMapping("/users/feign/slow")
    public User getSlowUserFeign() {
        return userService.getSlowUserFeign();
    }
}

Hystrix Dashboard

1 Configuration du Dashboard

Microservice Dashboard Hystrix :

hystrix-dashboard/pom.xml
<dependencies>
    <!-- Hystrix Dashboard -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
    </dependency>
    
    <!-- Actuator -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>
hystrix-dashboard/src/main/java/com/example/dashboard/HystrixDashboardApplication.java
package com.example.dashboard;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

/**
 * Application Hystrix Dashboard
 */
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardApplication.class, args);
    }
}
hystrix-dashboard/src/main/resources/application.properties
# === CONFIGURATION SERVEUR ===
server.port=9000
spring.application.name=hystrix-dashboard

# === CONFIGURATION DASHBOARD ===
hystrix.dashboard.proxy-stream-allow-list=*

# === CONFIGURATION ACTUATOR ===
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

2 Utilisation du Dashboard

Accès au Dashboard Hystrix :

Une fois démarré, le dashboard est accessible à l'URL : http://localhost:9000/hystrix

Pour monitorer un service, entrez l'URL du stream Hystrix : http://localhost:8080/actuator/hystrix.stream

Fonctionnalités du Dashboard :

  • Circuit Status : État des circuits (Ouvert/Fermé)
  • Error Rate : Taux d'erreurs en temps réel
  • Request Volume : Volume de requêtes
  • Latency : Temps de réponse
  • Fallbacks : Nombre d'appels fallback
  • Thread Pools : Utilisation des pools de threads

Configuration Avancée de Hystrix

1 Configuration Programmatique

Configuration via code :

serviceA/src/main/java/com/example/client/config/HystrixProgrammaticConfig.java
package com.example.client.config;

import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixThreadPoolProperties;
import org.springframework.context.annotation.Configuration;

/**
 * Configuration programmatique de Hystrix
 */
@Configuration
public class HystrixProgrammaticConfig {
    
    /**
     * Configuration des propriétés Hystrix par défaut
     */
    static {
        // Configuration du circuit breaker
        HystrixCommandProperties.Setter()
                .withCircuitBreakerEnabled(true)
                .withCircuitBreakerRequestVolumeThreshold(10)
                .withCircuitBreakerErrorThresholdPercentage(50)
                .withCircuitBreakerSleepWindowInMilliseconds(5000);
        
        // Configuration du thread pool
        HystrixThreadPoolProperties.Setter()
                .withCoreSize(10)
                .withMaximumSize(20)
                .withMaxQueueSize(100)
                .withQueueSizeRejectionThreshold(80);
        
        // Configuration des timeouts
        HystrixCommandProperties.Setter()
                .withExecutionTimeoutEnabled(true)
                .withExecutionTimeoutInMilliseconds(3000);
    }
}

2 Métriques et Monitoring

Collecte de métriques :

serviceA/src/main/java/com/example/client/metrics/HystrixMetricsService.java
package com.example.client.metrics;

import com.netflix.hystrix.HystrixCommandMetrics;
import com.netflix.hystrix.HystrixThreadPoolMetrics;
import org.springframework.stereotype.Service;

import java.util.Collection;

/**
 * Service pour collecter les métriques Hystrix
 */
@Service
public class HystrixMetricsService {
    
    /**
     * Récupère les métriques des commandes Hystrix
     */
    public void logCommandMetrics() {
        Collection<HystrixCommandMetrics> commandMetrics = HystrixCommandMetrics.getInstances();
        
        for (HystrixCommandMetrics metrics : commandMetrics) {
            System.out.println("=== Métriques pour la commande: " + metrics.getCommandKey().name() + " ===");
            System.out.println("Requêtes totales: " + metrics.getHealthCounts().getTotalRequests());
            System.out.println("Erreurs: " + metrics.getHealthCounts().getErrorCount());
            System.out.println("Taux d'erreurs: " + metrics.getHealthCounts().getErrorPercentage() + "%");
            System.out.println("Temps moyen: " + metrics.getExecutionTimeMean() + "ms");
            System.out.println("99ème percentile: " + metrics.getExecutionTimePercentile(99) + "ms");
            System.out.println();
        }
    }
    
    /**
     * Récupère les métriques des pools de threads
     */
    public void logThreadPoolMetrics() {
        Collection<HystrixThreadPoolMetrics> threadPoolMetrics = HystrixThreadPoolMetrics.getInstances();
        
        for (HystrixThreadPoolMetrics metrics : threadPoolMetrics) {
            System.out.println("=== Métriques du pool de threads: " + metrics.getThreadPoolKey().name() + " ===");
            System.out.println("Threads actifs: " + metrics.getCurrentActiveCount().intValue());
            System.out.println("Taille du pool: " + metrics.getCurrentPoolSize().intValue());
            System.out.println("Files d'attente: " + metrics.getCurrentQueueSize().intValue());
            System.out.println("Rejets de requêtes: " + metrics.getRollingCountThreadsRejected().intValue());
            System.out.println();
        }
    }
    
    /**
     * Vérifie l'état des circuits
     */
    public void logCircuitStates() {
        Collection<HystrixCommandMetrics> commandMetrics = HystrixCommandMetrics.getInstances();
        
        for (HystrixCommandMetrics metrics : commandMetrics) {
            System.out.println("=== État du circuit: " + metrics.getCommandKey().name() + " ===");
            System.out.println("État: " + (metrics.isCircuitBreakerOpen() ? "OUVERT" : "FERMÉ"));
            System.out.println("Requêtes dans la fenêtre: " + metrics.getRollingCount(HystrixCommandMetrics.HealthCounts.class).getTotalRequests());
            System.out.println();
        }
    }
}

Tests et Validation

1 Tests Unitaires

Tests de Hystrix :

serviceA/src/test/java/com/example/client/hystrix/GetUserCommandTest.java
package com.example.client.hystrix;

import com.example.client.model.User;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import org.junit.jupiter.api.Test;
import org.springframework.web.client.RestTemplate;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

class GetUserCommandTest {
    
    @Test
    void testSuccessfulExecution() {
        // Given
        RestTemplate restTemplate = mock(RestTemplate.class);
        Long userId = 1L;
        String serviceUrl = "http://service-b";
        User expectedUser = new User(userId, "Test User", "test@example.com", "TEST");
        
        when(restTemplate.getForObject(anyString(), eq(User.class)))
                .thenReturn(expectedUser);
        
        GetUserCommand command = new GetUserCommand(restTemplate, userId, serviceUrl);
        
        // When
        User result = command.execute();
        
        // Then
        assertNotNull(result);
        assertEquals(userId, result.getId());
        assertEquals("Test User", result.getName());
        verify(restTemplate).getForObject(anyString(), eq(User.class));
    }
    
    @Test
    void testFallbackExecution() {
        // Given
        RestTemplate restTemplate = mock(RestTemplate.class);
        Long userId = 1L;
        String serviceUrl = "http://service-b";
        
        // Simuler une exception
        when(restTemplate.getForObject(anyString(), eq(User.class)))
                .thenThrow(new RuntimeException("Service indisponible"));
        
        GetUserCommand command = new GetUserCommand(restTemplate, userId, serviceUrl);
        
        // When
        User result = command.execute();
        
        // Then
        assertNotNull(result);
        assertEquals(userId, result.getId());
        assertEquals("Utilisateur par défaut", result.getName());
        assertEquals("default@example.com", result.getEmail());
    }
    
    @Test
    void testCircuitBreakerOpens() {
        // Given
        RestTemplate restTemplate = mock(RestTemplate.class);
        String serviceUrl = "http://service-b";
        
        // Simuler plusieurs échecs
        when(restTemplate.getForObject(anyString(), eq(User.class)))
                .thenThrow(new RuntimeException("Service indisponible"));
        
        // When & Then - Exécuter plusieurs fois pour ouvrir le circuit
        for (int i = 0; i < 10; i++) {
            GetUserCommand command = new GetUserCommand(restTemplate, (long)i, serviceUrl);
            try {
                command.execute();
            } catch (HystrixRuntimeException e) {
                // Circuit breaker ouvert
                if (i >= 5) { // Après 5 erreurs, le circuit devrait s'ouvrir
                    System.out.println("Circuit breaker ouvert à l'itération " + i);
                }
            }
        }
    }
}

2 Tests d'Intégration

Tests avec Spring Boot :

serviceA/src/test/java/com/example/client/integration/HystrixIntegrationTest.java
package com.example.client.integration;

import com.example.client.model.User;
import com.example.client.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
@TestPropertySource(properties = {
    "eureka.client.enabled=false"
})
class HystrixIntegrationTest {
    
    @Autowired
    private UserService userService;
    
    @Test
    void testHystrixCommandWithFallback() {
        // Test avec un service qui tombe en panne
        // Hystrix devrait appeler le fallback
        
        User result = userService.getErrorUserHystrix();
        
        assertNotNull(result);
        // Vérifier que c'est l'utilisateur par défaut (fallback)
        assertEquals("Utilisateur par défaut", result.getName());
        assertEquals("default@example.com", result.getEmail());
    }
    
    @Test
    void testHystrixFeignWithFallback() {
        // Test avec Feign + Hystrix fallback
        
        User result = userService.getErrorUserFeign();
        
        assertNotNull(result);
        // Vérifier que c'est l'utilisateur d'erreur (fallback de Feign)
        assertEquals("Utilisateur d'erreur", result.getName());
        assertEquals("error@example.com", result.getEmail());
    }
    
    @Test
    void testHystrixTimeoutHandling() {
        // Test de la gestion des timeouts
        
        User result = userService.getSlowUserHystrix();
        
        assertNotNull(result);
        // Le fallback devrait être appelé en cas de timeout
        assertTrue(result.getName().contains("par défaut") || result.getName().contains("lent"));
    }
}

Tableau Récapitulatif des Fonctionnalités

Fonctionnalité Hystrix Resilience4j État
Circuit Breaker @HystrixCommand @CircuitBreaker ✅ Disponible
Retry Limité @Retry ✅ Amélioré
Rate Limiter Non @RateLimiter ✅ Nouveau
Bulkhead Thread Pools @Bulkhead ✅ Amélioré
Time Limiter Timeouts @TimeLimiter ✅ Nouveau
Monitoring Hystrix Dashboard Actuator + Métriques ✅ Évolué
Maintenance Déprécié (2018) Actif ⚠️ Hystrix / ✅ Resilience4j

Bonnes Pratiques et Conseils

1 Configuration Optimale

Configuration recommandée pour Hystrix :

# === CONFIGURATION HYSTRIX OPTIMALE ===

# Timeout raisonnable
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000

# Circuit breaker sensible
hystrix.command.default.circuitBreaker.requestVolumeThreshold=10
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=10000

# Pool de threads adapté
hystrix.threadpool.default.coreSize=20
hystrix.threadpool.default.maximumSize=50
hystrix.threadpool.default.maxQueueSize=100

# === CONFIGURATION SPÉCIFIQUE PAR COMMANDE ===
hystrix.command.GetUserCommand.execution.isolation.thread.timeoutInMilliseconds=2000
hystrix.command.CreateUserCommand.execution.isolation.thread.timeoutInMilliseconds=5000

2 Gestion des Fallbacks

Fallbacks robustes :

public class RobustUserCommand extends HystrixCommand<User> {
    
    @Override
    protected User run() throws Exception {
        // Logique normale
        return userService.getUserById(userId);
    }
    
    @Override
    protected User getFallback() {
        // Logging détaillé
        log.error("Fallback activé pour l'utilisateur {}: {}", userId, getFailedExecutionException().getMessage());
        
        // Métriques personnalisées
        metricsService.incrementFallbackCounter("getUserById");
        
        // Cache fallback
        User cachedUser = cacheService.getUserFromCache(userId);
        if (cachedUser != null) {
            return cachedUser;
        }
        
        // Données par défaut avec contexte
        return User.builder()
                .id(userId)
                .name("Utilisateur temporairement indisponible")
                .email("unavailable@example.com")
                .department("UNKNOWN")
                .build();
    }
}

3 Monitoring et Alertes

Alertes importantes :

  • Taux d'erreurs élevé : > 50% sur 10 requêtes consécutives
  • Circuit breaker ouvert : Indique un service en difficulté
  • Timeouts fréquents : Problème de performance
  • Fallbacks élevés : Impact sur l'expérience utilisateur
  • Saturation thread pool : Risque de blocage

Configuration des alertes :

@Component
public class HystrixAlertService {
    
    @EventListener
    public void onHystrixEvent(HystrixCommandExecutionEvent event) {
        if (event.getEventType() == HystrixEventType.FAILURE) {
            alertService.sendAlert("Hystrix failure detected: " + event.getCommandKey());
        }
    }
    
    @EventListener
    public void onCircuitBreakerEvent(HystrixCircuitBreakerEvent event) {
        if (event.getCircuitBreakerState() == CircuitBreakerState.OPEN) {
            alertService.sendAlert("Circuit breaker opened: " + event.getCommandKey());
        }
    }
}

Conclusion

Points Clés à Retenir

⚠️ Hystrix est DÉPRÉCIÉ

Utilisez Resilience4j pour les nouveaux projets !

graph TB A[Hystrix] --> B[Fonctionnalités] A --> C[Intégration] A --> D[Configuration] A --> E[Monitoring] A --> F[Migration] B --> B1[Circuit Breaker] B --> B2[Fallbacks] B --> B3[Timeouts] B --> B4[Thread Pools] C --> C1[RestTemplate] C --> C2[OpenFeign] C --> C3[Eureka] D --> D1[Properties] D --> D2[Programmatique] E --> E1[Dashboard] E --> E2[Métriques] E3[Alertes] F --> F1[Resilience4j] F2[Configuration] F3[Annotations] style A fill:#f44336,stroke:#d32f2f style B fill:#2196F3,stroke:#0D47A1 style C fill:#FF9800,stroke:#E65100 style D fill:#9C27B0,stroke:#4A148C style E fill:#4CAF50,stroke:#388E3C style F fill:#FF5722,stroke:#E64A19

Résumé historique :

  • Hystrix était la solution Netflix pour la tolérance aux pannes
  • Utilisait HystrixCommand pour encapsuler les appels distants
  • Fournissait des fallbacks pour gérer les échecs gracieusement
  • Incluait un Dashboard pour le monitoring en temps réel
  • Déprécié en 2018 au profit de solutions plus légères