Pour revenir à la première partie de l'architecture découplée grâce aux Rx, c'est par ici.

Hystrix pour une meilleure gestion des statuts des dépendances

Sur son site, Hystrix est décrit comme suit :

« Hystrix is a latency and fault tolerance library designed to isolate points of access to remote systems, services and 3rd party libraries, stop cascading failure and enable resilience in complex distributed systems where failure is inevitable. »

Soit une bibliothèque qui permet, dans une architecture distribuée, la gestion des latences et la tolérance aux pannes des systèmes en dépendance, en évitant la remontée en cascade de leurs statuts d’erreur de sorte à voir des services résilients au sein d’environnements où les pannes sont inévitables.

 

Nous allons avoir un petit aperçu de Hystrix à travers son usage dans notre exemple de composition de services, afin d’avoir une meilleure gestion des exceptions. Avec Hystrix, notre service peut s’écrire comme suit :

1     @Test
 2     @GET
 3     public void compositeService(@Suspended final AsyncResponse response) {
 4         Observable.fromCallable(() -> makeInterestingValue())
 5                 .flatMap(lastName -> {
 6                     Observable<String> aObs = new ServiceACommand(lastName).observe();
 7                     Observable<String> bObs = new ServiceBCommand(lastName).observe();
 8                     return Observable.zip(aObs, bObs, (a, b) -> format("=> Result : %s, %s", a, b);
 9                 })
10                 .subscribe(response::resume,
11                         (error) -> {
12                             response.resume(Response.serverError()
13                                     .entity(error.getMessage())
14                                     .build());
15                         });
16 
17     }

Aux lignes 6 et 7, nous avons introduit la construction de commandes Observable Hystrix à la place de la construction d’Observable de RxJava. L’autre changement dans ce code est le traitement de l’exception : toute occurrence d’exception dans le code est traitée comme une erreur HTTP 500, car rappelez-vous, l’une des fonctions principales de Hystrix est d’éviter la propagation des erreurs provenant des dépendances. Nous considérons donc qu’une exception provenant de notre commande Observable Hystrix est une erreur imprévue.

 

Inspectons à présent une des commandes Hystrix utilisée dans le service REST. Soit la commande d’appel du service A :

 1     private class ServiceACommand extends HystrixObservableCommand<String> {
 2         private String name;
 3 
 4         protected ServiceACommand(String name) {
 5             super(Setter.
 6                     withGroupKey(HystrixCommandGroupKey.Factory.asKey("work group"))
 7                     .andCommandKey(HystrixCommandKey.Factory.asKey("work"))
 8                     .andCommandPropertiesDefaults(
 9               HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(500)));
10             this.name = name;
11         }
12 
13         @Override
14         protected Observable<String> construct() {
15             return Observable.fromCallable(() -> wsA(name))
16                     .map(response -> response.readEntity(String.class));
17         }
18 
19         @Override
20         protected Observable<String> resumeWithFallback() {
21             return Observable.just("Service A too long");
22         }
23     }

La commande étend la classe HystrixObservableCommand avec comme argument du type générique le type de l’Observable à retourner par la commande Hystrix, ici String. Dans le constructeur on indique les paramètres de construction de la commande :

  • un groupe, une clé ( lignes 6 et 7)
  • une surcharge du timeout par défaut de la commande ( lignes 8 et 9).

La commande implémente la méthode abstraite construct() pour y spécifier la construction de l’Observable correspondant à l’action qu’elle doit exécuter.

Enfin la méthode resumeWithFallback() définit le mécanisme de fallback de la commande, c’est à dire l’action alternative à exécuter lorsque l’exécution de l’action normale de la commande est victime d’une exception.

 

Revenons-en à l’utilisation de la commande :

6                     Observable<String> aObs = new ServiceACommand(lastName).observe();

L’appel de la méthode observe() ne laisse pas transparaître un détail :  c’est qu’il conduit à l’exécution de la commande et au retour d’un Observable permettant de souscrire à l’exécution de la commande. Il existe une autre méthode de la classe HystrixObservableCommand.toObservable(), qui elle retourne juste un Observable permettant de souscrire à l’exécution de la commande. En résumé :

  • HystrixObservableCommand.tobserve() exécute la commande et retourne un Observable permettant de souscrire à son exécution.
  • HystrixObservableCommand.ttoObservable() retourne un Observable permettant de souscrire à l’exécution de la commande.

 

5.1 Circuit Breaker

Figure 5-1 Circuit Breaker Hystrix

Figure 5-1 Circuit Breaker

 

Hystrix implémente le pattern Circuit Breaker (coupe-circuit) dans le processus d’exécution des commandes. Le Circuit Breaker est un intercepteur des appels d’une commande Hystrix qui peut décider à un moment de ne pas lui déléguer les appels qu’il reçoit s’il juge que la commande n’est pas en état de les traiter. Ce jugement est basé sur les statistiques de succès/échec des derniers appels de la commande. La façon dont le Circuit Breaker exploite les statistiques pour faire son jugement peut être configurée (nombre d’appels échoués, durée d’isolement de la commande, etc.). Dans la terminologie du Circuit Breaker, on parle de circuit ouvert dans la situation où la commande est isolée par le  Circuit Breaker , et de circuit fermé lorsque la commande reçoit les requêtes.

Figure 5-2 Diagramme d'états-transitions d'une commande Hystrix

Figure 5-2 Diagramme d’états-transitions d’une commande Hystrix

 

L’ouverture du circuit a principalement pour avantage d’éviter de faire des appels éventuellement coûteux, aussi bien pour l’appelant que pour l’appelé. Lorsqu’il est ouvert, en fonction de son implémentation, le Circuit Breaker  se charge de retourner un résultat ou une erreur à l’appelant. Dans Hystrix, le Circuit Breaker se repose pour cela sur le mécanisme de fallback que nous avons évoqué.

Nous allons terminer cette partie sur Hystrix en présentant deux outils.

 

5.2 Hystrix Javanica

Hystrix Javanica propose des annotations permettant de simplifier l’usage de Hystrix.

 

5.3  Turbine

Turbine est une application de monitoring permettant l’agrégation de flux Server-Sent Event (SSE) en un flux unique. Il est utilisé dans le but de dégager des métriques (usage, percentile, taux de réussite, etc.) sur les usages des API des applications qui lui envoient des événements SSE.  Turbine est agnostique à la source des événements qui lui envoient des données, même si elle a été conçue spécifiquement dans le but d’agréger les métriques fournies par Hystrix. Netflix qui en est l’auteur l’utilise pour récolter les métriques fournies par les couches Hystrix de ses services, déployés entre 100 et 1000 machines. Turbine permet de présenter les métriques qu’elle agrège dans un dashboard.

Figure 5-3 Dashboard de Turbine Hystrix

Figure 5-3 Dashboard de Turbine

5.4 Références