Maintenant que nous avons vu la théorie sur les précédents articles disponibles ici et ici, penchons-nous sur la pratique.
Plusieurs solutions sont possibles pour l’implémenter. Par exemple en Java il existe des librairies qui le font pour nous comme :
Focalisons-nous sur Netflix Hystrix.
Hystrix est un framework open source libéré par Netflix en 2012 et intégré dans Spring Cloud.
Pour l’utiliser dans notre projet Java, rien de plus simple :
public class CallController extends HystrixCommand {
public CallController() {
super(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("MyGroup2"))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withCircuitBreakerEnabled(true)
.withExecutionTimeoutEnabled(true)
.withExecutionTimeoutInMilliseconds(5000)
.withExecutionIsolationThreadInterruptOnTimeout(true)
.withCircuitBreakerRequestVolumeThreshold(5)
.withCircuitBreakerSleepWindowInMilliseconds(1 * 60 * 1000)
.withMetricsRollingStatisticalWindowInMilliseconds(3 * 61 * 1000)));
@Override
protected String run() {
String message_distant;
Socket socket;
BufferedReader in;
try {
System.out.println(" Demande de connexion " + InetAddress.getLocalHost());
...
System.out.println(" Message distant " + message_distant);
socket.close();
} catch (UnknownHostException e) {
System.out.println("UnknownHostEXCEPTION");
throw new RuntimeException();
} catch (IOException e) {
System.out.println(" IOEXCEPTION");
// e.printStackTrace();
throw new RuntimeException();
}
if (message_distant == null) {
throw new RuntimeException();
}
return "-----" + message_distant + " -------";
}
@Override
protected String getFallback() {
return "---- Fallback : No Answer from distant Server ----- ";
}
Par exemple avec Apache Maven.
com.netflix.hystrix
hystrix-core
x.y.z
Comme nous venons de le voir, l’utilisation de Hystrix n’est pas très compliquée.
Les difficultés sont plutôt de pouvoir :
Sur les appels sortants, plusieurs solutions sont possibles pour les détecter :
Une fois l’implémentation réalisée, l’étape suivante est de la tester pour être sûr que tout marche comme convenu.
Nous allons séparer les tests en deux parties :
Si Netflix Hystrix est utilisé, un moyen simple de tester que tout fonctionne correctement lorsque le circuit breaker est ouvert, est d’utiliser la propriété forceOpen.
Une autre solution plus générique est d’utiliser un outil de mock où l’on peut scripter les réponses. WireMock est l’un d’eux. Il permet de mocker une API et de paramétrer les réponses (ajout de délai, réponse erronée…). Solution plus simple si nous ne voulons pas ajouter un nouvel outil dans le projet est de mocker la fonction de wrapping pour avoir le résultat attendu.
Mais la meilleure solution est de prévoir dans votre code, le moyen d’ouvrir à la demande le circuit breaker pour réaliser les tests le plus simplement possible (comme dans Hystrix et sa propriété forceOpen).
Cette étape ne doit pas être négligée, car pour avoir réalisé de nombreux tests de robustesse dans ma carrière, régulièrement j’ai eu des surprises (effet domino d’un crash qui s’étend à toute l’application, mécanisme d’éviction de la dépendance défaillante buggée, mécanisme de retour de la dépendance défaillante une fois le problème corrigé instable…).
De plus, réaliser ces tests a de nombreux avantages :
Quelques possibilités de test de robustesse :
Passons au concret avec quelques exemples, mais avant cela une présentation rapide de quelques outils est nécessaire.
JMeter est un outil libre qui permet d'effectuer des tests de performance sur des applications. Il permet de simuler le comportement de plusieurs utilisateurs agissant de manière simultanée. Il est aussi possible avec cet outil de conserver les résultats et de les enregistrer au format CSV et en base de données, par exemple InfluxDB.
Quelques fonctionnalités utiles lors de test de robustesse :
Chaos Monkey est un outil open source développé par Netflix pour tester le bon fonctionnement de son écosystème cloud. Sachant que les pannes sont inévitables, cet outil est destiné à stopper aléatoirement des instances de machines virtuelles et des services dans le but de détecter les points faibles de l'architecture mise en place. L'arrêt des machines simule d'hypothétiques pannes et permet de s'assurer que le système est construit avec un degré de redondance suffisant.
Ces librairies vont nous aider à injecter du code à la volée pendant l'exécution de notre application. Et bien sûr, le code injecté permettra de provoquer une défaillance :
Nous allons prendre pour exemple Byteman. (N’hésitez pas à lire sa documentation officielle pour plus d’informations.)
Byteman se présente sous la forme d’un agent Java (qui s'attache à la JVM avec le paramètre javaagent lors du lancement de notre application ou le programme bmjava.sh livré avec Byteman). Il ne nous reste plus qu'à injecter des Rules
Une Rules se compose :
Et lors de l'exécution de notre application, à chaque exécution de la méthode ciblée, notre code sera lui aussi exécuté.
Exemple de perte de connexion à la base de données :
RULE JdbcOwnerRepositoryImpl.findById throw an exception
CLASS JdbcOwnerRepositoryImpl
METHOD findById
AT ENTRY
IF true
DO throw new org.springframework.dao.DataRetrievalFailureException("Probleme de connexion a la base de donnees")
ENDRULE
Exemple d’augmentation du temps de réponse :
RULE Wait in OwnerController.processFindForm entry
CLASS OwnerController
METHOD processFindForm
AT ENTRY
IF true
DO Thread.sleep(8000)
ENDRULE
De nombreux outils livrés avec votre système d’exploitation préféré peuvent aider à créer des défaillances.
Par exemple sous Linux :
Exemple de simulation de perte de paquet avec la commande tc :
tc qdisc add dev wlan0 root netem loss 10%
Et qui dit test dit supervision afin de comprendre ce qui se passe.
Pour cela, nous allons utiliser :
Maintenant que tout est en place, il ne nous reste plus qu'à implémenter nos tests de robustesse en suivant ce modèle.
Avec les parties suivantes :
Comme on peut le voir, JMeter joue aussi le rôle d'orchestration en activant et désactivant la défaillance pendant que l’application est sous charge.
Résumons le tout avec des schémas.
Étape 1 : JMeter simule des utilisateurs jusqu'à la vitesse de croisière (charge atteinte, application et temps de réponse stables).
Étape 2 : JMeter demande au simulateur de défaillance de générer un problème (perte du réseau, crash d’un service…) et envoie l’heure exacte du déclenchement dans l’outil de supervision.
Ensuite nous laissons tourner assez longtemps pour récupérer toutes les informations nécessaires pour l'analyse et le bon déclenchement du circuit breaker.
Étape 3 : Nous corrigeons la défaillance pour vérifier que tout revient à l’état prévu.
Et comme pour tous les tests, n’oubliez pas d’automatiser ce qui est possible (attention au coût de maintenance) pour les jouer souvent.
Nous savons maintenant comment tester le circuit breaker.
Nous verrons lors du prochain et dernier article comment le superviser et ses limites.
Pour aller plus loin, notre nouveau livre blanc sur le sujet vient de sortir :