Guide sur le cloud printanier Netflix - Hystrix

Guide sur le cloud printanier Netflix - Hystrix

1. Vue d'ensemble

Dans ce didacticiel, nous aborderons lesSpring Cloud Netflix Hystrix - la bibliothèque de tolérance aux pannes. Nous utiliserons la bibliothèque et implémenterons le modèle d'entrepriseCircuit Breaker, qui décrit une stratégie contre les échecs en cascade à différents niveaux dans une application.

Le principe est analogue à l'électronique:Hystrix surveille les méthodes pour les appels en échec aux services associés. En cas d'échec, le circuit est ouvert et l'appel est transmis à une méthode de secours.

La bibliothèque tolérera les échecs jusqu’à un seuil. Au-delà, le circuit reste ouvert. Ce qui signifie qu’il transmettra tous les appels ultérieurs à la méthode de secours afin d’éviter les pannes futures. This creates a time buffer for the related service to recover from its failing state.

2. Producteur REST

Pour créer un scénario illustrant le modèleCircuit Breaker, nous avons d'abord besoin d'un service. Nous l'appellerons «REST Producer», car il fournit des données pour les «REST Consumer» compatibles Hystrix, que nous créerons à l'étape suivante.

Créons un nouveau projetMaven en utilisant la dépendancespring-boot-starter-web:


    org.springframework.boot
    spring-boot-starter-web
    1.4.0.RELEASE

Le projet lui-même est intentionnellement gardé simple. Il se compose d'une interface de contrôleur avec une méthode GET annotée@RequestMapping renvoyant simplement unString, a@RestController implémentant cette interface et un@SpringBootApplication.

Nous allons commencer par l'interface:

public interface GreetingController {
    @GetMapping("/greeting/{username}")
    String greeting(@PathVariable("username") String username);
}

Et la mise en place:

@RestController
public class GreetingControllerImpl implements GreetingController {

    @Override
    public String greeting(@PathVariable("username") String username) {
        return String.format("Hello %s!\n", username);
    }
}

Ensuite, nous allons écrire la classe d’application principale:

@SpringBootApplication
public class RestProducerApplication {
    public static void main(String[] args) {
        SpringApplication.run(RestProducerApplication.class, args);
    }
}

Pour compléter cette section, la seule chose à faire est de configurer un port d’application sur lequel nous allons écouter. Nous n’utiliserons pas le port par défaut 8080 car il devrait rester réservé à l’application décrite à l’étape suivante.

De plus, nous définissons un nom d’application pour pouvoir rechercher nos «REST Producer» à partir de l’application cliente que nous présenterons plus tard. Créons unapplication.properties avec le contenu suivant:

server.port=9090
spring.application.name=rest-producer

Nous pouvons maintenant tester nos «REST Producer» à l’aide de curl:

$> curl http://localhost:9090/greeting/Cid
Hello Cid!

3. Consommateur REST avecHystrix

Pour notre scénario de démonstration, nous allons implémenter une application Web, qui consomme le serviceREST de l'étape précédente en utilisantRestTemplate etHystrix. Par souci de simplicité, nous l'appellerons les «REST Consumer».


    org.springframework.cloud
    spring-cloud-starter-hystrix
    1.1.5.RELEASE


    org.springframework.boot
    spring-boot-starter-web
    1.4.0.RELEASE


    org.springframework.boot
    spring-boot-starter-thymeleaf
    1.4.0.RELEASE

Pour que leCircuit Breaker fonctionne,Hystix analysera les classes annotées de@Component or @Service pour les méthodes annotées de@HystixCommand, implémentera un proxy pour celui-ci et surveillera ses appels.

Nous allons d'abord créer une classe@Service, qui sera injectée dans un@Controller. Puisque nous construisons unweb-application using Thymeleaf,, nous avons également besoin d'un modèle HTML pour servir de vue.

Ce sera notre@Serviceinjectable implémentant un@HystrixCommand avec une méthode de repli associée. Cette solution de secours doit utiliser la même signature que l'original:

@Service
public class GreetingService {
    @HystrixCommand(fallbackMethod = "defaultGreeting")
    public String getGreeting(String username) {
        return new RestTemplate()
          .getForObject("http://localhost:9090/greeting/{username}",
          String.class, username);
    }

    private String defaultGreeting(String username) {
        return "Hello User!";
    }
}

RestConsumerApplication sera notre principale classe d'application. L'annotation@EnableCircuitBreaker analysera le chemin de classe pour toute implémentation compatible deCircuit Breaker.

Pour utiliserHystrix explicitement, vous devez annoter cette classe avec@EnableHystrix:

@SpringBootApplication
@EnableCircuitBreaker
public class RestConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(RestConsumerApplication.class, args);
    }
}

Nous allons configurer le contrôleur à l'aide de nosGreetingService:

@Controller
public class GreetingController {

    @Autowired
    private GreetingService greetingService;

    @GetMapping("/get-greeting/{username}")
    public String getGreeting(Model model, @PathVariable("username") String username) {
        model.addAttribute("greeting", greetingService.getGreeting(username));
        return "greeting-view";
    }
}

Et voici le modèle HTML:



    
        Greetings from Hystrix
    
    
        

Pour nous assurer que l'application écoute sur un port défini, nous mettons ce qui suit dans un fichierapplication.properties:

server.port=8080

Pour voir un disjoncteur Hystix en action, nous commençons notre «REST Consumer» et nous pointons notre navigateur vershttp://localhost:8080/get-greeting/Cid. Dans des circonstances normales, les éléments suivants seront affichés:

Hello Cid!

Pour simuler un échec de nos «REST Producer», nous l’arrêterons simplement, et après avoir terminé d’actualiser le navigateur, nous devrions voir un message générique, renvoyé par la méthode de secours dans nos@Service:

Hello User!

4. Consommateur REST avecHystrix etFeign

Maintenant, nous allons modifier le projet de l'étape précédente pour utiliserSpring Netflix Feign comme client REST déclaratif, au lieu de SpringRestTemplate.

L'avantage est que nous pourrons plus tard facilement refactoriser notre interface client Feign pour utiliserSpring Netflix Eureka pour la découverte de services.

Pour démarrer le nouveau projet, nous allons faire une copie de nos «REST Consumer», et ajouter les «REST Producer» etspring-cloud-starter-feign en tant que dépendances:


    com.example.spring.cloud
    spring-cloud-hystrix-rest-producer
    1.0.0-SNAPSHOT


    org.springframework.cloud
    spring-cloud-starter-feign
    1.1.5.RELEASE

Maintenant, nous pouvons utiliser nosGreetingController pour étendre un client Feign. Nous allons implémenter le repli deHystrix en tant que classe interne statique annotée avec@Component.

Alternativement, nous pourrions définir une méthode annotée@Bean renvoyant une instance de cette classe de secours.

La propriété name des@FeignClient est obligatoire. Il est utilisé, pour rechercher l'application soit par découverte de service via unEureka Client, soit via une URL, si cette propriété est donnée. Pour en savoir plus sur l'utilisation de Spring Netflix Eureka pour la découverte de services, consultezat this article:

@FeignClient(
  name = "rest-producer"
  url = "http://localhost:9090",
  fallback = GreetingClient.GreetingClientFallback.class
)
public interface GreetingClient extends GreetingController {

    @Component
    public static class GreetingClientFallback implements GreetingController {

        @Override
        public String greeting(@PathVariable("username") String username) {
            return "Hello User!";
        }
    }
}

Dans lesRestConsumerFeignApplication, nous allons mettre une annotation supplémentaire pour permettre l'intégration deFeign, en fait,@EnableFeignClients, à la classe d'application principale:

@SpringBootApplication
@EnableCircuitBreaker
@EnableFeignClients
public class RestConsumerFeignApplication {

    public static void main(String[] args) {
        SpringApplication.run(RestConsumerFeignApplication.class, args);
    }
}

Nous allons modifier le contrôleur pour utiliser un client Feign câblé automatiquement, plutôt que les@Service précédemment injectés, pour récupérer notre message d'accueil:

@Controller
public class GreetingController {
    @Autowired
    private GreetingClient greetingClient;

    @GetMapping("/get-greeting/{username}")
    public String getGreeting(Model model, @PathVariable("username") String username) {
        model.addAttribute("greeting", greetingClient.greeting(username));
        return "greeting-view";
    }
}

Pour distinguer cet exemple du précédent, nous allons modifier le port d'écoute de l'application dans lesapplication.properties:

server.port=8082

Enfin, nous allons tester ce «REST Consumer» activé pourFeign comme celui de la section précédente. Le résultat attendu devrait être le même.

5. Cache de secours avecHystrix

Maintenant, nous allons ajouter Hystrix à notre projetSpring Cloud. Dans ce projet de cloud, nous avons un service d’évaluation qui communique avec la base de données et obtient l’évaluation des livres.

Supposons que notre base de données est une ressource sous demande et que sa latence de réponse peut varier dans le temps ou ne pas être disponible dans le temps. Nous allons gérer ce scénario avec lesHystrixCircuit-Breaker retombant dans un cache pour les données.

5.1. Configuration et configuration

Ajoutons la dépendancespring-cloud-starter-hystrix à notre module de notation:


    org.springframework.cloud
    spring-cloud-starter-hystrix

Lorsque les notes sont insérées / mises à jour / supprimées dans la base de données, nous les répliquerons dans le cache Redis avec unRepository. Si vous souhaitez en savoir plus sur Redis, cochezthis article.

Mettons à jour lesRatingService pour envelopper les méthodes d'interrogation de la base de données dans une commande Hystrix avec@HystrixCommand et le configurer avec un retour à la lecture depuis Redis:

@HystrixCommand(
  commandKey = "ratingsByIdFromDB",
  fallbackMethod = "findCachedRatingById",
  ignoreExceptions = { RatingNotFoundException.class })
public Rating findRatingById(Long ratingId) {
    return Optional.ofNullable(ratingRepository.findOne(ratingId))
      .orElseThrow(() ->
        new RatingNotFoundException("Rating not found. ID: " + ratingId));
}

public Rating findCachedRatingById(Long ratingId) {
    return cacheRepository.findCachedRatingById(ratingId);
}

Notez que la méthode de secours doit avoir la même signature qu'une méthode encapsulée et doit résider dans la même classe. Maintenant, lorsque lefindRatingById échoue ou est retardé de plus d'un seuil donné,Hystrix revient àfindCachedRatingById.

Comme les capacités deHystrix sont injectées de manière transparente en tant que conseils deAOP, nous devons ajuster l'ordre dans lequel les conseils sont empilés, au cas où nous aurions d'autres conseils comme les conseils transactionnels de Spring. Ici, nous avons ajusté le conseil AOP de transaction de Spring pour avoir une priorité inférieure à l'avis deHystrix AOP:

@EnableHystrix
@EnableTransactionManagement(
  order=Ordered.LOWEST_PRECEDENCE,
  mode=AdviceMode.ASPECTJ)
public class RatingServiceApplication {
    @Bean
    @Primary
    @Order(value=Ordered.HIGHEST_PRECEDENCE)
    public HystrixCommandAspect hystrixAspect() {
        return new HystrixCommandAspect();
    }

    // other Beans, Configurations
}

Ici, nous avons ajusté l'avis deSpring’s transactionAOP pour avoir une priorité inférieure à l'avis deHystrix AOP.

5.2. Test du repli Hystrix

Maintenant que nous avons configuré le circuit, nous pouvons le tester en supprimant la base de donnéesH2 avec laquelle notre référentiel interagit. Mais d'abord, exécutons l'instanceH2 en tant que processus externes au lieu de l'exécuter en tant que base de données intégrée.

Copions leH2 library (h2-1.4.193.jar) dans un répertoire connu et démarrons le serveur H2:

>java -cp h2-1.4.193.jar org.h2.tools.Server -tcp
TCP server running at tcp://192.168.99.1:9092 (only local connections)

Mettons à jour l'URL de la source de données de notre module enrating-service.properties pour pointer vers ce serveur H2:

spring.datasource.url = jdbc:h2:tcp://localhost/~/ratings

Nous pouvons démarrer nos services comme indiqué dans nos précédentsarticle de la série Spring-Cloud, et tester les évaluations de chaque livre en supprimant l'instance H2 externe que nous exécutons.

Nous avons pu voir que lorsque la base de donnéesH2 n'est pas accessible,Hystrix revient automatiquement àRedis pour lire les notes de chaque livre. Le code source illustrant ce cas d'utilisation peut être trouvéhere.

6. Utilisation des étendues

Normalement, une méthode annotée@HytrixCommand est exécutée dans un contexte de pool de threads. Mais parfois, il doit être exécuté dans une portée locale, par exemple, un@SessionScope ou un@RequestScope. Cela peut être fait en donnant des arguments à l'annotation de commande:

@HystrixCommand(fallbackMethod = "getSomeDefault", commandProperties = {
  @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE")
})

7. Le tableau de bord deHystrix

Une fonctionnalité facultative intéressante deHystrix est la possibilité de surveiller son état sur un tableau de bord.

Pour l'activer, nous allons mettrespring-cloud-starter-hystrix-dashboard etspring-boot-starter-actuator dans le pom.xml de nos "REST Consumer":


    org.springframework.cloud
    spring-cloud-starter-hystrix-dashboard
    1.1.5.RELEASE


    org.springframework.boot
    spring-boot-starter-actuator
    1.4.0.RELEASE

Le premier doit être activé en annotant un@Configuration avec@EnableHystrixDashboard et le second active automatiquement les métriques requises dans notre application Web.

Après avoir redémarré l'application, nous pointerons un navigateur surhttp://localhost:8080/hystrix, saisirons l'URL des métriques d'un «hystrix.stream» et commencerons la surveillance.

Enfin, nous devrions voir quelque chose comme ceci:

image

Surveiller un «hystrix.stream» est quelque chose de bien, mais si vous devez regarder plusieurs applications activées pourHystrix, cela deviendra peu pratique. Pour cela,Spring Cloud fournit un outil appeléTurbine, qui peut agréger les flux pour les présenter en unHystrix Dashboard.

La configuration deTurbine dépasse le cadre de cet article, mais la possibilité doit être mentionnée ici. Il est donc également possible de collecter ces flux via la messagerie, en utilisantTurbine Stream.

8. Conclusion

Comme nous l'avons vu jusqu'à présent, nous sommes maintenant en mesure de mettre en œuvre le modèleCircuit Breaker en utilisantSpring Netflix Hystrix avecSpring RestTemplate ouSpring Netflix Feign.

Cela signifie que nous sommes en mesure de consommer des services avec un repli inclus à l’aide de données «statiques» ou plutôt «par défaut» et de surveiller l’utilisation de ces données.

Comme d'habitude, vous trouverez les sources surGitHub.