Accueil > Non classé > JMeter : utiliser les journaux d’accès

JMeter : utiliser les journaux d’accès

JMeter est un outil de test de performances qui dispose de nombreux composants par défaut, dont certains restent assez méconnus malgré leur grande utilité. Cet article présente l’échantillonneur Journal d’accès, qui permet de lire des access logs Apache pour rejouer les requêtes qui y sont consignées. En effet, la documentation sur ce composant est assez succincte, ainsi que le tutoriel (PDF) – du genre concis, voire expéditif – ce qui ne met pas en valeur la chose.

Cas d’utilisation

Pour réaliser un test de charge réaliste sur une application web, ou tenter de reproduire un problème observé en production, il est nécessaire de disposer d’un scénario de test (collection de requêtes initiées par des utilisateurs) représentatif de ce qui se passe effectivement dans l’environnement de production. Un jeu de test irréaliste peut donner des tests trompeurs : reproduire les conditions de la production, c’est se donner un environnement le plus semblable possible, mais aussi faire subir à l’installation les mêmes sollicitations.

La situation est stable sous la charge... mais que teste-t-on au juste ?

La situation semble rester stable sous la charge... mais que teste-t-on au juste ?

Selon le type d’application dont il s’agit, les subtilités à considérer ne sont pas les mêmes : une application de gestion comportant principalement des formulaires ne se teste pas de la même façon qu’une application dont la vocation est de servir des contenus. Dans le premier cas, ce sont les informations saisies par les utilisateurs qui peuvent être difficiles à simuler, et dans le second c’est la grande multiplicité des pages visibles.

Dans ce dernier cas précisément, l’exercice est moins trivial qu’il n’y paraît : si la majorité des requêtes ne fait pas intervenir de données saisies par l’utilisateur, ce qui facilite grandement la tâche. C’est la multiplicité des pages visibles qui constitue un problème. En effet, comment générer une séquence de requêtes représentative de ce que les utilisateurs consultent ? Choisir des URLs pseudo-aléatoirement à l’aide d’un algorithme n’est pas simple – et montrer que les échantillons ainsi choisis sont représentatifs l’est encore moins – et en choisir un nombre réduit, en capturant la navigation d’un petit nombre d’utilisateurs, ne permettrait pas de rendre compte de la variété des pages vues en production : cela fausserait la réaction des composants qui y sont sensibles (typiquement les mécanismes de cache).

Dans le cas où l’application est déjà en production, il existe une alternative : capturer la navigation des utilisateurs en production, de façon à avoir le scénario de test le plus réaliste qui soit – et pour cause, il s’agit d’un scénario réel. La chose est cependant contraignante à mettre en place dans un environnement qui se veut performant ; une approche moins coûteuse consiste à utiliser les access logs, ou journaux d’accès, que fournit par défaut tout serveur Apache. Ces journaux donnent, pour chaque requête traitée, un certain nombre d’informations : parmi elles, sauf configuration fantaisiste, on trouve la méthode (GET, POST…) et l’URL de la requête. Ces informations permettent de rejouer les actions effectuées en production, d’autant plus fidèlement que les sessions des utilisateurs jouent un rôle négligeable, c’est-à-dire que toutes les informations décidant de la réponse obtenue sont contenues dans la seule URL.

Mise en place

Récupérer des logs

La première chose à faire, outre se procurer JMeter, est de récupérer ces fameux journaux d’accès. On placera sur le système de fichier local une sélection de logs jugée intéressante (une journée entière, un intervalle de quelques heures durant lequel un problème a été observé en production, etc.).

L’exemple qui servira pour cet article est le suivant :

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1 - - [12/Nov/2009:12:55:45 +0100] "GET /jmeterTest1.html HTTP/1.1" 200 137 "-" "Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.14) Gecko/2009090216 Ubuntu/9.04 (jaunty) Firefox/3.0.14"
127.0.0.1 - - [12/Nov/2009:12:55:46 +0100] "GET /jmeterTest2.html HTTP/1.1" 200 137 "http://localhost/jmeterTest1.html" "Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.14) Gecko/2009090216 Ubuntu/9.04 (jaunty) Firefox/3.0.14"
127.0.0.1 - - [12/Nov/2009:12:55:46 +0100] "GET /jmeterTest3.html HTTP/1.1" 200 137 "http://localhost/jmeterTest1.html" "Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.14) Gecko/2009090216 Ubuntu/9.04 (jaunty) Firefox/3.0.14"
127.0.0.1 - - [12/Nov/2009:12:55:47 +0100] "GET /jmeterTest4.html HTTP/1.1" 200 137 "http://localhost/jmeterTest1.html" "Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.14) Gecko/2009090216 Ubuntu/9.04 (jaunty) Firefox/3.0.14"
127.0.0.1 - - [12/Nov/2009:12:55:47 +0100] "GET /jmeterTest5.html HTTP/1.1" 200 137 "http://localhost/jmeterTest1.html" "Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.14) Gecko/2009090216 Ubuntu/9.04 (jaunty) Firefox/3.0.14"
127.0.0.1 - - [12/Nov/2009:12:55:48 +0100] "GET /jmeterTest6.html HTTP/1.1" 200 137 "http://localhost/jmeterTest3.html" "Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.14) Gecko/2009090216 Ubuntu/9.04 (jaunty) Firefox/3.0.14"
127.0.0.1 - - [12/Nov/2009:12:55:48 +0100] "GET /jmeterTest7.html HTTP/1.1" 200 137 "http://localhost/jmeterTest5.html" "Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.14) Gecko/2009090216 Ubuntu/9.04 (jaunty) Firefox/3.0.14"
127.0.0.1 - - [12/Nov/2009:12:55:49 +0100] "GET /jmeterTest8.html HTTP/1.1" 200 137 "http://localhost/jmeterTest6.html" "Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.14) Gecko/2009090216 Ubuntu/9.04 (jaunty) Firefox/3.0.14"
127.0.0.1 - - [12/Nov/2009:12:55:49 +0100] "GET /jmeterTest9.html HTTP/1.1" 200 137 "http://localhost/jmeterTest1.html" "Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.14) Gecko/2009090216 Ubuntu/9.04 (jaunty) Firefox/3.0.14"
127.0.0.1 - - [12/Nov/2009:12:55:49 +0100] "GET /jmeterTest10.html HTTP/1.1" 200 137 "http://localhost/jmeterTest1.html" "Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.14) Gecko/2009090216 Ubuntu/9.04 (jaunty) Firefox/3.0.14"
127.0.0.1 - - [12/Nov/2009:13:01:57 +0100] "GET /jmeterTest11.html HTTP/1.1" 404 250 "http://localhost/jmeterTest1.html" "Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.14) Gecko/2009090216 Ubuntu/9.04 (jaunty) Firefox/3.0.14"

Ces lignes décrivent des accès à des pages HTML volontairement ordonnées, de façon à bien voir comment JMeter rejoue le scénario. On observe que de nombreuses informations sont présentes : IP du client, date, méthode, URL, code de retour, etc.

Configuration JMeter

Configuration JMeter

Configurer JMeter

Dans un contrôleur logique de type “boucle”, on place un “échantillon journal d’accès”. La boucle permet de jouer plusieurs lignes du journal : sans elle, on ne récupère qu’un échantillon par utilisateur. Un “tableau de résultats” permettra de visualiser rapidement la liste des échantillons prélevés. Notons qu’il est possible de placer, avant la boucle, un échantillon de login, pour authentifier les utilisateurs JMeter : c’est un moyen simple de s’assurer que les contenus sont visibles (et effectivement servis lors du test) dans une application gérant des droits d’accès.

La configuration de l’échantillon est assez simple. On définit d’abord une cible (serveur et port) et on indique le fichier de logs à utiliser. Ensuite il faut préciser les 2 paramètres décidant du comportement du composant : l’analyseur et le filtre.

L’analyseur

Il s’agit de la stratégie adoptée pour la lecture du fichier de logs. Les implémentations proposées par défaut peuvent être regroupées en 2 catégories :

  • soit chaque utilisateur déroule le fichier de logs, chacun de son côté, ce qui fait que les requêtes sont jouées chacune autant de fois qu’il y a d’utilisateurs (TCLogParser),

    Résultats d'un run utilisant le TCLogParser (boucle paramétrée à 5 itération) : chaque utilisateur a joué les 5 premières lignes du log.

    Avec le TCLogParser et une boucle paramétrée à 5 itérations : chaque utilisateur a joué les 5 premières lignes du log.

  • soit le fichier de logs est parcouru une seule fois, et chaque utilisateur vient y lire une ligne lorsqu’il doit passer à l’action : dans ce cas, chaque ligne du journal peut être attribuée à un utilisateur différent (SharedTCLogParser).

Avec le SharedTCLogParser : on a joué 10 requêtes différentes, réparties sur les 2 utilisateurs.

Avec le SharedTCLogParser : on a joué 10 requêtes différentes, réparties sur les 2 utilisateurs.

Le filtre

Sur les deux implémentations proposées, une seule est utilisable depuis l’interface graphique : le SessionFilter, qui permet de s’assurer que deux lignes du log correspondant à la même IP cliente ne requêtent pas le serveur simultanément (simulant le fait qu’un utilisateur n’enchaîne pas les pages sans attendre le rendu du navigateur). On peut en revanche spécifier sa propre implémentation de filtre, pour peu qu’elle ait été placée dans le classpath de JMeter : on pourra ainsi éviter de jouer les requêtes qui tombent en 404 ou 500, par exemple.

Critique

L’échantillon “journal d’accès” est particulièrement adapté au test de certaines applications de par le fait que les journaux d’accès ne fournissent pas beaucoup d’informations complémentaires sur chaque requête : on ne connaît rien de la session courante ou des données transmises lors d’un POST, notamment ; de sorte que l’URL et la méthode d’une requête doivent suffire à elles seules à récupérer la réponse voulue. JMeter étant capable de s’authentifier, on peut négliger la problématique des droits d’accès pour peu qu’on dispose d’un compte ayant tous les droits requis (ce qui est souvent le cas dans un environnement de test).

Pour ce type d’applications, il est donc possible de réaliser des requêtes très semblables à celles qui sont envoyées en production. La principale limitation réside dans la distribution temporelle des échantillons. En effet, on n’a aucun contrôle précis sur le débit des échantillonnages : les dates sont présentes dans le journal d’accès, mais pas prises en compte.

Une solution consiste à placer dans le contrôleur logique “boucle” un compteur de temps : on simule alors des pauses entre chaque requête, d’une durée dépendant du compteur choisi. Un compteur de temps fixe ou aléatoire permet d’aérer l’échantillonnage un peu arbitrairement ; un compteur de débit constant permet de tenir une certaine charge cible, quels que soient les temps de réponse de l’application. Dans les deux cas, on propose un rythme qui n’est pas celui que décrit le journal d’accès : les pics de charge ne sont pas reproductibles. Pour un test de charge, on peut souvent se contenter d’un débit maximal, mais ça n’est pas toujours possible (débit trop élevé pour tenir la charge continûment) et jamais réaliste (si on veut observer un comportement précisément lors du pic).

Petite remarque concernant le SharedTCLogParser, liée au fait que le lien entre les pages HTML et les ressources qui y sont décrites n’est pas conservé dans les logs Apache : l’observation (durées moyennes, débit, etc.) peut être biaisée pendant le déroulement du test. Il est possible en effet d’observer de très bon résultats dans un premier temps, puis des temps qui se dégradent bizarrement, une moyenne et un écart-type qui explosent vers la fin du test. Ce comportement peut survenir dans le cas où de nombreuses ressources sont servies très rapidement alors que d’autres, bien que plus rares, souffrent de problèmes de performances : les pages les plus lentes bloquent les quelques utilisateurs qui les traitent, alors que les autres sont traitées très rapidement et font apparaître un débit et des temps remarquables. Ce n’est que lorsque les réponses les plus lentes arrivent que leur impact se fait sentir, et la dynamique du test en est complètement faussée. Attention donc aux mauvaises interprétations, surtout si on souhaite observer des choses dans le feu de l’action…

Share
Categories: Non classé Tags: , , ,
  1. Pas encore de commentaire
  1. 23/07/2016 à 19:00 | #1