jeudi 29 août 2013

Le test de performance en JAVA / JEE


But du guide

Ce petit article a pour objectif de guider à travers les tests de performances sur une application JAVA/JEE. Je le rédige pour retranscrire mon expérience et peut être éviter à certains les écueils que j'ai rencontré. La tâche étant ardue, je ne serai pas exhaustif. De plus, ce guide étant rédigé au fur et à mesure, il faut toujours le considérer comme un work in progress.

Optimiser ou valider une capacité ?

Un test de performance est une opération difficile si on ne la prend pas par le bon bout. Il faut d’abord savoir ce que l’on cherche : cherche-t-on des optimisations pour améliorer le temps de réponse d’une application,  ou cherche-t-on à valider la capacité d’une application ?
En règles général, les deux axes sont intéressants, mais il convient de séparer les analyses pour ne pas se perdre.
Optimiser
Pour trouver des optimisations, la représentativité n’est pas nécessaire. Vous pouvez effectuer et valider des optimisations sur votre poste à l'aide de votre eclipse.
Test de capacité
Pour vérifier qu’une application est capable de supporter une charge à X utilisateurs, la représentativité est fondamentale.Il faut obligatoirement disposer de ressources identiques à celles utilisée en production.

Pré-requis

Les moyens

Dans l’analyse de tout phénomène complexe, l’analyse déductive est limitée par de multiples biais. C’est particulièrement vrai pour l’étude des performances d’un programme. 
En effet, l’état du réseau, la le serveur et ses processus en arrière plan, sont autant de biais dans le résultat final. La variabilité d’un résultat peut être grande. Il faut donc:
  • Essayer au maximum de disposer de ressources fraiche qui ne sont pas polluées par de multiples installation. Le mieux est de disposer d'une machine neuve ou fraichement masterisée pour le test. 
  • Répéter plusieurs voir les tests avant de conclure
  • S’assurer que la configuration sur laquelle s'effectue les tests ne varie pas. Il ne faut pas installer de nouveau programme en cours de route.
Statistiquement, l’écart type mesure la dispersion moyenne d’un résultat. On considère que l’écart type caractérise une information valide si elle ne dépasse pas 25% de la valeur moyenne.

Outils

Il existe une bonne quantité d'outils pour l'analyse de performance. Nous nous concentrons sur les gratuits.

JVisualVM:


Est un outil qui permet d'observer la machine virtuelle. Il fournit notamment les indications clés pour la consommation de performance.
- Mémoire
- CPU
- Thread
- Entrée sortie réseau

L'écran à gauche permet de sélectionner l'application que vous souhaiter observer. 



Resources monitor windows :


JMeter :


 

Optimiser

Méthodologie

La méthodologie pour optimiser est classique. A partir d'une observation, nous effectuons une analyse, puis nous effectuons des propositions, ces propositions doivent être implémentées puis testées.

Préparer un test

Dans les environnements de développements normaux, les applications sont plus réactives qu'en production. Il existe plusieurs raison à cela:
- Il n'y a qu'un utilisateur
- Les données en base ne sont pas représentatives forcément représentative d'une volumétrie
- Quand une personne développe un programme, elle est souvent indulgente et tolérante à une réactivité lente.

Recréer l'ensemble de ces conditions peut être compliqué, mais il faut consacrer un minimum d'effort à rendre le phénomène observable. Par exemple, lorsqu'on cherche à améliorer un temps de réponse et que notre poste sert une page en un temps raisonnable. Il faut détériorer la situation pour que la lenteur soit visible.

Observation

Le but de l'observation est de déterminer quelle est la ressource critique. S'il s'afit d'optimiser le temps de réponse, nous observerons particulièrement le CPU, le nombre d'entrée sortie, le nombre de thread en attente.

Observer le CPU: 


Pour observer le CPU, on utilise le moniteur windows. Cette information nous permettent de déterminer quelle est l'élément incriminé dans la consommation excessive. Dans le cas le plus courant, il s'agit de déterminer si la consommation est à imputer à la DB ou au CPU. Nous l'illustrons dans les captures d'écran suivantes:

 Consommation excessive de CPU côté JAVA capture système
Dans cette capture nous voyons que le processus qui consomme le plus de processeur est javaw.exe. Il consomme 30%. 

Consommation excessive de CPU côté DB
Dans cette capture, nous voyons que la consommation excessive de CPU est faite par le process mysqld.exe. Hormis cela, le processus JAVA consomme 1.5% de la puissance totale.

Observer les entrées sorties:

Pour observer les entrées sorties, on utilise l'onglet thread de JVisual VM.

Observer la mémoire:

Pour observer la mémoire on utilise les onglets mémoire de JVisualVM.

Analyser

Si la consommation de CPU est excessive dans le JVisual VM. Cela signifie que l'amélioration est à apporter côté JAVA. Si la consommation CPU est surtout côté DB, cela signifie que l'amélioration est à apporter côté base de données. L'expérience nous apprend que:
La consommation excessive de CPU est souvent lié:
- Mauvaise utilisation de la base de données
- A l'utilisation de boucle inutiles
- A la nécessité de dénormaliser

Mauvaise utilisation de la base de données

Boucles inutiles

A l'utilisation de boucle inutiles
Exemple: 
Pour effectuer un rapprochement dans un tableau, on effectue la jointure par le parcours des deux tableaux résultant en un traitement de complexité nxn.

Solutions: 
Utiliser la base de données pour effectuer le rapprochement des données
- A l'utilisation d'une brique technologique qui n'est pas adéquate dans le contexte.

Dénormalisation
Exemple:
Pour  travailler sur une structure arborescente, on utilise la notion générique de parent. Ces solutions sont performantes pour traiter des arbres de profondeur arbitrairement grande. Cependant,dans l'industrie, nous nous trouvons souvent face à des structures arborescentes de profondeur limitée (<10 p="">TODO: Présenter une structure de données qui utilise un arbre générique. 
Solution:Dans ce cas, il est préférable d'utiliser une seule table pour représenter l'index.
TODO: Présenter une structure de données qui utilise un arbre dénormalisé. 

En se basant sur le code tester et trouver des optimisations en local a partir d’un test sans stimulation excessive (1 utilisateur).
Stimuler unitairement les fonctions du serveur en local




Valider une capacité

Méthodologie

-          Estimer la stimulation de manière réaliste
-          Monter un environnement représentatif de la production
-          Trouver les ressources capables de simuler la charge effectuée par des clients.
Dans les deux cas, il faudra surveiller les ressources :

  • Consommation de CPU
  • Mémoire
  • Débit réseau et nombre de socket ouvertes
  • Vitesse du disque

Tester et trouver des optimisations

Les tests de performance sont souvent entrepris lorsqu’un problème apparait. On sait donc souvent par où il faut commencer.

Identifier la fonction critique

Sans médire, il se trouve que la plupart des utilisateurs vont vous dire: "L'application est lente" et c'est la seule information que vous aurez. Il faudra donc:
Pour décrypter la grogne, voici :

  • Est-ce que une page en particulier ralentit toute l'application en consommant toutes les ressources ou est-ce que la lenteur est générale ?
Pour répondre à cette question, il faut tester unitairement  les différentes fonctions de l'application.

Accentuer le trait

Charger le serveur pour obtenir des résultats plus significatifs. 

Tester unitairement la ressource critique


En lançant le sampler de JVisual VM, nous voyons les ressources qui consomment du temps (self time), et celle qui concernent les ressources processeur (self time CPU).
Dans l’exemple ci-dessous, nous voyons que le processeur d’arrière plan du tomcat, consomme beaucoup de temps (self time), en revanche, il ne consomme pas de temps au niveau du processeur. En revanche, la connexion JDBC (ligne en dessous) consomme à la fois du self time et du self time (CPU). C’est le signe infaillible d’un problème de base de données.

Base de données


  • Vérifier que le nombre de requêtes n’est pas excessif
  •  Vérifier que le processeur n’est pas trop sollicité
  •   Vérifier la longueur des requêtes

Identifier les fonctions goulet d’étranglement qui sont évidents sans charge particulières.
Utiliser un réseau WIFI accentue les lenteurs liée au nombre de requêtes, cela peut être un bon moyen pour constater une amélioration des performances.