Soirée JVM Performance – Paris JUG

Lors de la soirée « JVM Performance » organisée le 11/09/2012 par le ParisJUG, William Louth (@williamlouth sur twitter et william (at) inspired.com par email) de la société jinspired nous a sensibilisé à la problématique d'exploitation de la JVM.

Constat

Il part du constat, qu'en général, lorsqu'une alerte est détectée sur une JVM (alerte rouge nagios par exemple), la réaction des exploitants est :

  • Attendre que cela respasse au vert !
  • Faire un kill -9 de la jvm

Les exploitants ont finalement peu d'outil et ne peuvent pas vraiment analyser ce qui se passe à un instant T sur la JVM. La problématique est encore plus grande lorsqu'on essaye de connaître la qualité de service ressentie par les utilisateurs alors qu'on est en mode distribué dans le « cloud ».

Les logiciels que nous développons ne prennent pas en compte l'environnement dans lequel ils tournent (puissance de chaque nœud par exemple). Les traitements ne s'adaptent pas à l'environnement mais essayent de délivrer un résultat le plus rapidement possible.

Par exemple, lorsqu'on doit persister une information dans une base de données, le goulot d'étranglement étant cette base, il est probable qu'à sollicitation importante, certains Threads commencent à se bloquer. Or, un Thread qui se bloque, c'est de la mémoire de la JVM qui reste utilisée et on imagine très bien quelle sera la conséquence pour le Garbage Collector (GC) qui va commencer à passer de plus en plus souvent pour libérer de la mémoire.  Le GC va consommer de plus en plus de ressource processeur et au final, les Threads se bloqueront définitivement (spirale infernale).

L'idée défendue par William peut consister par exemple à ralentir certains traitements lorsqu'on constate que les accès à la base commencent à ralentir. Il s'agit d'implémenter une auto régulation par le logiciel lui même (Software Regulation Control).

La collecte

Pour cela, il est nécessaire de collecter de l'information sur les différents traitements et de les observer dans la durée afin de pouvoir anticiper l'avenir probable si rien est fait.

Le coût de la collecte

Le problème ici est que la collecte d'information et son stockage est très coûteux. William a illustré par un exemple assez simple mais très illustratif.


public class Client {
public static void main(String[] args) {
int NBTEST = 100;

long all = 0;
for (int i=0; i < NBTEST; i++) {
long start = System.nanoTime();
c1();
all += System.nanoTime() - start;
}
System.out.println("Mean: " + all / NBTEST);
}

public static void c1() {
}
}

L'objectif de ce code est de mesurer le temps d'exécution de l'appel à c1().

Sur mon PC portable assez lent, on obtient une moyenne de l'ordre de 1,5 ms.

Mais quel est le coût de cette mesure ?

Pour cela, William ajoute simplement (et seulement), deux appels à nanoTime() dans la méthode c1() qui devient donc :


public static void c1() {
System.nanoTime();
System.nanoTime();
}

La moyenne passe à environ 3 ms sur la même machine. On peut facilement conclure que le temps de la collecte est assez important.

Imaginez maintenant ce qui peut se passer lorsque les traitements s'empilent :


public static void c1() {
System.nanoTime();
c2();
System.nanoTime();
}

public static void c2() {
System.nanoTime();
c3();
System.nanoTime();
}

public static void c3() {
System.nanoTime();
c4();
System.nanoTime();
}

public static void c4() {
System.nanoTime();
System.nanoTime();
}

Notre système de mesure va introduire une gène dans la capacité de nos JVM à réaliser les vrais traitements pour les utilisateurs. Ici en l’occurrence, la méthode c4() ne fait toujours rien et pourtant nous avons consommé du temps ! Imaginez qu'il faille en plus persister les données collectées dans un fichier de log ou une base ou que sais-je encore !

L'idée développée par William est qu'il faut tenter de minimiser cet effort de collecte. Comment ?

Tracer l'utile seulement

La première piste pourrait être de ne tracer que là où on constate des ralentissements. Par exemple, se placer à une couche interface (servlet) que l'on peut imaginer être notre méthode c1() et mesurer tous les temps de chaque niveau c1() de notre application. Puis, lorsqu'on constate un incident, on regarde quel c1() a posé problème et alors, dans ce cas, on ajoute du code sur tous les niveaux c2() sollicités par ce c1(). On relivre l'application en production et on attend à nouveau que cela se dégrade puis ainsi de suite jusqu'à identifier le problème au niveau le plus fin.

Évidemment, cette méthode n'est pas envisageable dans un contexte de production !

Le crédit-temps pour les traces

L'autre piste consiste plutôt à s'attribuer une sorte de « crédit-temps » de perturbation et à dynamiquement adapter les mesures à chaque itération de collecte. Cela signifie que pour la première itération, on va s'attacher à évaluer un c1() en dessous, un c2() et un c3(). Puis, voyant notre crédit-temps épuisé, on va stopper là la collecte pour cet appel. A l'appel suivant, on partira du même c3() puis on ira vers c4() et on prendra un autre c1(). Et ainsi de suite. A bout de quelques itérations, nous devrions avoir les mesures pour l'ensemble de notre système.

L'axe du temps est donc utilisé pour collecter de la donnée et identifier les goulots d'étranglement avec un temps de nuisance maîtrisé !

Une amélioration possible consiste à essayer d'éliminer le bruit inutile de notre collecte. En effet, si nous savons par expérience qu'un traitement (niveau c2() par exemple) est de l'ordre de 1 ms en moyenne, et que lors d'une mesure niveau c2(), nous constatons que nous avons moins de 1,2 ms par exemple, il est alors inutile d'aller collecter les données dans les sous-niveaux puisque nous savons déjà que tout se déroule correctement.

A l'inverse, si notre c2() commence à dériver dans le temps, il va être intéressant de collecter l'information sur ses sous-niveaux pour lesquels nous appliquerons la même stratégie de collecte.

Au bout de quelques itérations, la collecte devrait être ciblée vers les points « anormaux » de notre traitement et donc, être efficace à 99 % !

Si nous arrivons à identifier les méthodes coûteuses, il est ensuite plus aisé de demander aux développeurs d'optimiser ces parties de code. Optimiser ne veut pas dire nécessairement d'améliorer la dite méthode mais peut-être de regarder au dessus si l'appel est véritablement nécessaire.

Par exemple, si nous reprenons notre exemple, l'appel à la méthode c4() est sans doute inutile puisque c4() ne fait rien ! On ne peut pas optimiser d'avantage c4() mais on peut se passer de son appel !

C'est le même principe par exemple pour une requête SQL. La requête peut-être optimisée mais jusqu'à un certain stade seulement. Pour améliorer ensuite, il faudra regarder à un niveau plus haut dans la couche des services.

Adapter le logiciel

Une fois la collecte réalisée, nous pouvons tenter de nous en servir pour auto-adapter notre logiciel à son environnement.

Par exemple, William prend le cas d'un utilisateur qui arrive sur un site assez chargé. Il ouvre la première page et patiente une dizaine de secondes. Il ouvre une seconde et une troisième et attend à chaque fois une dizaine de secondes. De fait, il se dit que ce site « craint ».

L'idée serait alors de fabriquer des lignes prioritaires (comme les FAST LANE dans les aéroports) de façon à ce qu'un utilisateur qui a déjà patienté une fois voit son prochain traitement prioritaire par rapport à d'autres n'ayant jamais patienté.

Pour cela, il va être nécessaire d'être capable d'allouer la puissance de traitement dynamiquement en fonction du contexte. Ce qui veut dire ralentir certains traitements au profit d'autres (on peut imaginer des sleeps par exemple).

A la manière de ce qu'on connaît avec la gestion de projet et le fameux « chemin critique », on peut identifier dans certains traitements le chemin critique qui implique que tout retard dans l'un de ses composants entraînera un retard total de la chaîne. Un Thread n'a pas cette notion. Lorsqu'on lui demande un traitement, il va tenter de l'exécuter le plus vite possible sans tenir compte du contexte environnant. Or, il peut ne pas être sur le chemin critique et peut s'exécuter plus tardivement.

Conclusion

William a donc exposé pendant près de deux heures la problématique de la collecte d'information sur l'état de la JVM et proposé des pistes pour adapter nos logiciels à cet état plutôt que d'observer impuissants la dégradation de nos JVM (se soldant en général par un arrêt-relance un peu brutal).

Les outils d'instrumentation que sa société propose permettent de répondre à ces besoins : Voir le site www.jinspired.com. Attention, le coût n'est pas anodin (à partir de 5k€ par CPU et licence globale pour un site à 115k€) sur lesquels il faut ensuite ajouter un coût de maintenance.

Toutefois, le rapport coût / efficacité peut s'avérer intéressant sur du long terme et peut-être grâce au tuning live vous faire économiser l'acquisition de quelques machines supplémentaires pour tenir la charge ! A étudier donc.

Sonar au JUG de Paris

Depuis plus d'un an, Sonar est l'un de outils clé de la plateforme d'intégration continue de Netapsys (au même titre qu'Hudson et Artifactory).

Olivier Gaudin, l'un de ses créateurs, en fera une présentation le 15 septembre 2009 au Paris JUG à l'occasion de la "Soirée Qualité du Logiciel" tel qu'indiqué sur le blog de Sonar.

Le JUG se tiendra dans les locaux de l'ISEP, vous pouvez vous y inscrire en suivant ce lien.

Paris JUG : soirée de présentation de Java EE 6 le mardi 13 janvier

Le thème de la première réunion de l'année du JUG parisien était Java EE 6. Antonio Goncalves, co-fondateur du JUG, a présenté les nouveautés apportés à Java Enterprise Edition en tant que membre du JCP, le Java Community Process. Il s'agit du groupe chargé de définir les spécifications de Java dans son ensemble. La première version de Java EE (en 1999) comportait 10 spécifications, la version 6 qui sortira en mai ou juin 2009 en comporte 28.

Paris JUG – GWT

"Bonsoir à tous ! Alors tout d'abord, il y a eu un petit bug sur l'application d'inscription qui doit normalement limiter les inscrits à 200, ce soir nous en avons 239 !"

Mais qu'est-ce qui peut bien rassembler autant de monde un mardi soir ? Facile, la réponse est dans le titre.

Groovy : retour sur la présentation du Paris JUG

Le JUG de Paris présentait le 9 septembre le langage Groovy et le framework associé Grails. Près de 200 personnes y assistaient dont, au moins, trois représentantes de la gente féminine !

Piètre programmeur java et amoureux de musique 'soul', je dois avouer que ma curiosité a d'abord porté sur le caractère "funky groove" du nom du langage et de son logo.


Cela dit, quand un vieux bonhomme de 37 balais comme moi discute de Groovy avec une jeune recrue et sort :
"Oh là là, encore un nouveau langage à apprendre" et que ce dernier répond
"Oui, mais c'est comme du java",
la crise naissante de la quarantaine m'a motivé à titiller mes neurones en comprenant mieux l'intérêt porté par les développeurs et ainsi, objectif ultime, de rester "hype" 😉

1er JUG2B au ParisJUG : essai transformé !

La première réunion du JUG2B, créé par le ParisJUG, a eu lieu ce soir. Qualité de l'intervention, nombre de participants, qualité des échanges, convivialité, ... cette première réunion est un vrai succès. Le JUG2B est lancé, et bien lancé !

Le JUG2B se veut moins technique et plus éclectique que les réunions traditionnelles du JUG. La soirée, intitulée "meilleurs développeurs", est animée par John Rizzo, fondateur de JavaBlackBelt.

Maven au ParisJUG

J'ai assisté mardi soir dernier à la présentation, par Arnaud Heritier, de Maven au Paris JUG . C'était une présentation vraiment intéressante. Arnaud a répondu avec beaucoup d'intelligence et de gentillesse à un certain nombre de questions concernant la communauté, l'organisation des repositories, les plugins et l'avenir du projet. Il a présenté les principales nouveautés de la version 2.0.9 et l'actualité de la communauté.

JUG2B : le ParisJUG étend son périmètre d’action

paris jugPar définition, un JUG (Java User Group), concentre naturellement les adeptes les plus ferrus de Java : du développeur au chef de projet technique en passant par l'architecte. Mais c'est bien connu, la consanguinité peut rapidement générer quelques problèmes... ! 😉
L'équipe du ParisJUG l'a bien compris et a décidé, à peine 4 mois après le lancement du JUG, de l'ouvrir à une population plus large : c'est le cadre du JUG2B (JUG to Business). Son objectif est d'associer les dirigeants (informatiques, ressources humaines, ...) à la démarche et de rassembler l'ensemble des acteurs tous les trimestres, autour de thèmes plus larges que lors des réunions mensuelles. Rassurez-vous, il y est toujours question d'informatique !

La première session du JUG2B, prévue le 12 juin, abordera deux thèmes intitulés "Meilleurs développeurs" et "Comment attirer et garder les développeurs".

Plus d'infos sur le site du JUG.