Tests de performance au JUG Toulouse

  • Sharebar

Le Mercredi 07 Décembre 2011, j’ai participé à la soirée du Toulouse JUG à l’Epitech, et il y avait deux présentatrices, “Speakeuses” comme elles préfèrent le dire, ou “Duchesses” en clin d’œil au “Java Duke” :

  • Claude FALGUIÈRE [1] qui nous a captivés pendant 1 heure chargée dans le vaste monde des tests de performance,
  • Marianne JULLIEN qui nous a fait un retour de Devoxx 2011 avec Claude.

Je vais dédier ce post au premier sujet.

J’avoue qu’au début je m’attendais à un tas de noms d’outils de profiling d’applications et d’un  ensemble de pratiques prêtes à appliquer comme pack de test pour toute application. Finalement, je me suis retrouvé très vite dans une ambiance à la Cluedo! Notre maître de jeu a dix ans d’expériences dans le domaine, et elle utilise littéralement les mots “investigation” et “scène de crime” pour nous le présenter.

Changer votre approche

Son premier conseil face à un problème de performances c’est d’oublier qu’il y a “une solution générique magique”, et d’éviter les faux amis :

  • ne pas partir directement sur son IDE afin d’optimiser trois lignes de code ou quelques boucles,
  • ne pas dire que chaque partie A, B et C du système est bien optimisée, donc le tout devrait l’être,
  • ne pas dire que des solutions plus chères, plus complexes et/ou plus à la mode feront le nécessaire ; clusteriser son application ou la mettre sur le cloud peut empirer la situation ou entraîner d’autres problèmes.

Au contraire, il faut :

  • bien définir la problématique et les objectifs qui varient selon le métier de l’application,
  • localiser le problème : ça arrive à qui ? quelle partie de l’application? dans quelle(s) config(s)? quand et comment ça se manifeste?
  • mener une étude approfondie et détaillée de tous les symptômes sans s’arrêter à la toute première piste et avant de se lancer dans l’apport des corrections.

Il faut savoir que la raison derrière le problème est souvent logique une fois trouvée, mais en même temps elle est souvent contre-intuitive : “Ah! mais pour nous le cache servait à accéder plus rapidement aux objets les plus consultés par nos clients.. On n’a jamais pensé qu’il allait ralentir l’application parce qu’il est mal configuré!”

Bien définir les objectifs

Un exercice difficile mais indispensable avant de commencer les investigations. Il faut savoir si la problématique qu’on a est :

  • un besoin de montée en charge : pour lequel une adaptation est indispensable mais simplement pas encore faite,
  • un changement dans l’exploitation de l’application : elle était en intranet et maintenant elle est plus exposée, …
  • un ralentissement : un vrai ou juste une impression subjective chez l’utilisateur final qui est souvent gêné au-delà d’1/2 seconde d’attente, et facilement “occupable” avec une petite animation,
  • une volonté de réduction de charges et donc de ressources : justifiable à laquelle il faut s’adapter, ou très contraignante face à laquelle il n’y a rien à faire.

Localiser le problème

C’est la phase centrale et la plus laborieuse bien sûr. Elle consiste à :

  1. Établir les faits,
  2. Analyser et formuler des hypothèses,
  3. Vérifier avec des mesures,
  4. Boucler sur ces trois :)

Plein de questions ensuite :

  • Qui ? revient à mieux connaître les utilisateurs qui se plaignent,
  • Combien? leur nombre, notamment le nombre de ceux qui travaillent en même temps,
  • Quoi ? la nature de leurs tâches : fréquentes (consultation des produits), vitales (recherches complexes) ou sensibles (paiement),
  • Quand ? le problème est récurrent (dates, heures, …) ou occasionnel (le Samedi soir, pendant les fêtes, …),
  • Où ? le problème arrive : sur quel serveur, dans quel zone géographique, …
  • Comment? le problème se manifeste brusquement (après une montée de charge) ou graduellement, il se manifeste sous forme de manque de ressources, lenteur ou interruption de services…

Répondre à chacune de ces questions est déjà pas mal, mais l’idéal c’est d’agréger les réponses, chronologiquement par exemple en montrant comment la situation a évolué.

Investigations

Cette phase consiste à étudier les symptômes et les indices collectés afin de localiser le problème dans l’application. D’abord, il faut s’assurer au niveau macro que l’application fonctionne dans les bonnes conditions : sur le bon système, pas de contraintes réseau, pas de problèmes de quota disque, mémoire ou CPU, etc.

Ensuite, il faut étudier les différents composants de l’application au niveau micro. Et cela nécessite une meilleure compréhension de son architecture. Une congestion locale peut être à l’origine d’une globale. Par exemple, une application web implique de bout en bout :

  • un navigateur,
  • un serveur http,
  • un serveur d’applications,
  • un serveur de base de données.

Chaque composant a sa configuration et sa complexité. A l’intérieur du serveur http ou le serveur d’applications lui-même, il s’agit souvent d’une architecture complexe de pools, de caches et de heap cachés. En plus, il faut penser à identifier tous les éventuels inculpés branchés sur le système : les services tiers, le batch de la BD, le serveur LDAP ou le serveur JMS peuvent nuire silencieusement à la santé de l’application.

Du côté hardware, on a souvent une autre architecture également hétérogène et complexe, formée par des routeurs, pares-feu et machines contraignantes. Cette architecture mérite aussi d’être étudiée et testée.

Pour cela, on sera amené à tester chaque partie de notre architecture, exactement comme les techniciens de réseaux qui testent une ligne téléphonique corrompue avec leurs matos, ils testent chaque petit bout : la différence est que nous n’avons pas le matériel unique et magique qui nous permet de tester de la même manière chaque partie en disant “oui/non” à la fin du test!

Plutôt, on se base sur quelques métriques afin de localiser le problème. D’abord, il faut vérifier si le problème arrive côté client ou serveur. Des outils comme YSlow, Page Speed, Charles ou Fiddler (HTTP Proxy) donnent le temps d’exécution de requêtes côté serveur et le temps nécessaire à l’affichage. Avec les interfaces riches qu’on commence à avoir avec du Javascript, une bonne partie de traitement est faite à l’affichage dans certaines applications.

Côté serveur, il faut utiliser d’autres outils de mesure :

  • Logs : nombre de pools, connexions et requêtes,
  • Trace des requêtes par couche (HTTP, réponses JSF, JDBC…),
  • Monitoring : Apache server-status,
  • Outils d’introspection : performasure, inrtoscope, Infrared,
  • Drivers JDBC Virtuels : IP6Spy, Log4JDBC
  • JMX,
  • Netstat.

Avec ces mesures, vous pouvez constater pour une requête qui prend 20s par exemple, si elle passe 17s sur un traitement spécifique ou un accès particulier ou extensif à la base de données. Vous pouvez détecter s’il s’agit d’une congestion au niveau des pools de connexions, s’il y a des connexions qui ne sont pas fermées correctement, etc.

Patterns

Pour éviter de se lancer dans ces mesures dans tous les sens, il vaut mieux avoir une idée basique de la cause du problème. Pour cela, notre “speakeuse” a présenté trois catégories de symptômes classées en fonction du nombre d’utilisateurs du système qui travaillent en même temps quand le problème de performance arrive :

  1. Une personne : pensez aux calculs répétitifs dans l’application, au temps d’attente subjectif, aux contraintes de volumétrie, à l’attente chez les produits tiers,
  2. Un groupe : soupçonnez la concurrence,
  3. Une foule : vérifier s’il y a une limitation de ressources ou une saturation quelque part dans le système.

Le premier cas, c’est là où les développeurs peuvent se régaler avec le profiling :) Pour y arriver il faut boucler en persévérance sur la proposition d’une hypothèse, le raffinement par l’investigation puis la vérification par le test.

Dans le cas d’accès concurrent, il faut savoir s’il s’agit d’une synchronisation sur un lock BD ou Java. Une attente indéfinie peut venir d’un dead/live-lock. Un “thread dump” analysé avec un outil d’analyse comme MAT, JCA, HealthCenter ou Samourai permettra de détecter ce genre de problèmes en Java.

De toute façon, il n’y a pas une seule solution magique, mais le bon sens consiste à :

  • utiliser les structures de données thread-safe plutôt que des synchronized, en faisant attention aux structures qui sont synchronized par défaut en Java (Vector, HashTable…)
  • quand ce n’est pas évitable, il faut réduire la taille des zones critiques, i.e. garder le lock autour de la plus petite portion de code,
  • et bien sûr, éviter la gestion des locks à la main.

Dans le cas de lenteur avec la montée de charge, pensez aux limites de ressources en mémoire, CPU, réseau et au nombre d’utilisateurs par licence. Des solutions peuvent être apportées en configurant l’exploitation de ces ressources à condition de ne pas excéder les vraies limites permises! Parce que, d’un côté ça peut coûter cher, et de l’autre ça ne servira à rien quand il y a une vraie limite physique. Par exemple, augmenter la valeur du paramètre -Xmx ne sert à rien s’il y a une quota par processus sur la machine! D’autres éléments configurables dans nos applications : la taille des pools, la taille des caches, le nombre d’instances, de connexions, leurs timeout, la configuration du Garbage Collector, etc. Des conseils plus détaillés sur ces paramétrages étaient donnés dans les diapos [2].

Les tests préventifs

Tout ce qui était dit jusque-là, sert de méthodologie de résolution de problèmes de performances. Mais, ça n’empêche en aucun cas d’anticiper. Surtout quand il s’agit d’une application critique et/ou quand la société veut protéger son chiffre d’affaire et son image.

Le meilleur chemin pour l’anticipation est la simulation. Il ne faut pas avoir peur du déploiement. C’est mieux de pouvoir tester en environnement pré- ou pseudo-production après chaque partie développée dans l’application. A condition de tester en milieux très proche de la réalité! Ça veut dire tester :

  • une bonne volumétrie de données,
  • avec des jeux de tests riches et représentatifs,
  • pendant un temps significatif,
  • en settings réels (même gestion d’erreurs, scénarios tordus, etc.)

Pour conclure

Privilégier le “fonctionnel” du produit à sa “rapidité”. Tout d’abord, il faut que votre application réponde bien aux attentes des utilisateurs en terme d’ergonomie et de fonctionnalités. La “robustesse” et la “stabilité” viennent après le fonctionnel mais également avant la rapidité. Sinon, pour avoir cette dernière, vous avez toutes les démarches qu’on vient de citer et décrire dans ce post.

Merci au Toulouse JUG pour cette excellente soirée.

[1] blog de Claude Falguière :
http://cfalguiere.wordpress.com/

[2] une présentation similaire à la notre mais faite à Genève :
http://www.parleys.com/#st=5&id=2695&sl=135
http://www.slideshare.net/claude.falguiere/diagnostic-performancessuisse

About Bilal Said

Genigraph e-Citiz R&D - Toulouse
This entry was posted in Conferences, Java, Test and tagged . Bookmark the permalink.

One Response to Tests de performance au JUG Toulouse

  1. Pingback: [Toulouse JUG] Retour sur la soirée Performances - www.kanithael.net

Leave a Reply