TUTORIELS 
PHP et les systèmes de caches
(Fourni par Direction|PHP)

Page 1 | 2 | 3 | 4

L'essentiel sur les différents systèmes, et une mise en application du caching avec PEAR/Cache_Lite
 (Janvier 2004)
 
Mais assez de théorie, passons maintenant à la pratique !

Utilisation de PEAR/Cache/Lite.php
* A LIRE Les solutions de caching en PHP4
* Le caching pour accélérer et alléger le réseau
(JDNet Solutions)
Prenons un exemple très simple. Supposons, pour l'illustration de notre situation, que dans votre page, vous affichiez la liste des entiers divisibles par 7 et inférieurs à 10000.
On peut écrire cela de la façon suivante :

<?php
$result = '';
for($i = 0 ; $i<10000 ; $i++) {
  if (($i % 7)==0) {
    $result = $result . $i;
    $result = $result . '<br>';
    }
  }
// Tout le contenu de la page est en
// fait dans $result
echo($result);
?>


Si on utilise PEAR/Cache_Lite pour mettre en cache la page, on écrit le code suivant :

<?php
// On charge Cache_Lite
require_once('Cache/Lite.php');

// On fixe un identifiant pour la page
$id = 'divisible_par_7';

// On définit quelques options :
// - le répertoire où seront stockés les fichiers de cache
// - la durée de vie du cache (ici 30 secondes)
$options = array(
  'cacheDir' => '/tmp/',
  'lifeTime' => 30
  );

// On crée un objet Cache_Lite avec les options précédentes
$Cache_Lite = new Cache_Lite($options);

// Si la page est dans le cache...
if ($data = $Cache_Lite->get($id)) {
  // ... on affiche le contenu du cache
  echo($data);
  // Si la page n'est pas dans le cache...
  }
else {
    // ...on calcule la page
    // On retrouve donc le source précédent...
    $result = '';
    for($i = 0 ; $i<10000 ; $i++) {
      if (($i % 7)==0) {
        $result = $result . $i;
        $result = $result . '<br>';
      }
    }
  echo($result);
  // ... mais à la fin, on n'oublie pas de stocker le résultat
  // dans le cache !
  $Cache_Lite->save($result);
  }
?>

Je suppose que les commentaires suffisent et qu'il n'est pas la peine de tout détailler, instruction par instruction. Globalement, on voit que l'on a inclut le premier script dans la gestion du cache du second.

Quid des performances ? Pour donner un ordre d'idée, j'ai fait quelques tests de performances sur la machine avec laquelle j'écris cet article en utilisant les commandes :

ab -c 10 -n 1000 http://127.0.0.1/test.php

(qui permet de soumettre apache à un flot de 1000 requêtes par vague de 10 simultanées) Les résultats sont donc les suivants :
- sans cache => apache sert environ 30 requêtes par seconde ;
- avec cache => apache sert environ 150 requêtes par seconde.

On note une spectaculaire envolée des performances, avec la gestion du cache activée. Le rapport entre les deux dépend bien évidemment du type de la page cachée (plus la page est gourmande, plus l'utilisation d'un cache est recommandée).
En contrepartie, outre le fait qu'il a fallu un peu allonger le script original, c'est que la page est considéré comme statique pendant la durée de validité du cache : 30 secondes dans notre exemple. Ce n'est pas un problème pour des pages qui évoluent peu mais pour un forum par exemple, ça oblige à trouver des astuces : soit on efface simplement le cache à chaque message posté, soit on utilise une gestion très fine de l'identifiant de la page : cela sort du cadre de cet article.

Utilisation de PEAR/Cache/Lite/Output.php
Le lecteur attentif aura noté bien des cas où il n'est pas possible d'appliquer la méthode présentée ci-dessus. En effet, il faut pour cela arriver à récupérer dans une variable unique tout le contenu de la page. Dans notre exemple, c'est la variable $result. Cela n'est effectivement pas toujours possible, parce qu'on utilise des scripts externes que l'on ne contrôle pas ou tout simplement parce que nos pages ne sont pas construites de cette façon. Heureusement, la classe PEAR/Cache/Lite/Output.php vient à notre secours.
Commençons par réécrire le script original sans passer par une variable $result :

<?php
for($i = 0 ; $i<10000 ; $i++) {
  if (($i % 7)==0) {
    echo($i);
    echo('<br>');
    }
  }
?>

Avec Cache_Lite_Output, cela devient:

<?php
// On charge Cache_Lite
require_once('Cache/Lite/Output.php');

// On fixe un identifiant pour la page
$id = 'divisible_par_7';

// On définit quelques options :
// - le répertoire où seront stockés les fichiers de cache
// - la durée de vie du cache (ici 30 secondes)
$options = array(
  'cacheDir' => '/tmp/',
  'lifeTime' => 30
  );

// On crée un objet Cache_Lite_Output avec les options précédentes
$Cache_Lite_Output = new Cache_Lite_Output($options);

// Si la page n'est pas en cache...
if (!($Cache_Lite_Output->start($id))) {
  // ... alors on lance le script original
  for($i = 0 ; $i<10000 ; $i++) {
    if (($i % 7)==0) {
      echo($i);
      echo('<br>');
      }
    }
  // marque la fin du script original
  $Cache_Lite_Output->end();
  }
?>

On note que le script original est simplement encapsulé dans la gestion du cache. On note également que le cas où la page est dans le cache, est automatiquement pris en charge par le gestionnaire, ce dernier s'occupant d'envoyer les données au navigateur. Quant aux performances, on obtient :
- sans cache => environ 35 requêtes par seconde ;
- avec cache => environ 145 requêtes par seconde.

Voyons maintenant un deuxième cas qui se pose souvent : vous voulez cacher deux blocs dans une page mais pas la page dans son intégralité. Poursuivons sur notre exemple. Notre page affichera donc la liste des entiers inférieurs à 10000 et divisible par 7 (bloc à cacher) puis l'heure courante (à ne pas cacher) et enfin la liste des entiers inférieurs à 10000 et divisible par 9 (bloc à cacher). A des fins d'illustration, on définira une durée de vie de 30 secondes pour le premier bloc et de 45 secondes pour le deuxième. Voilà une solution :

<?php
// On charge Cache_Lite
require_once('Cache/Lite/Output.php');

// On fixe un identifiant pour le premier bloc
$id1 = 'divisible_par_7';

// On fixe un identifiant pour le deuxième bloc
$id2 = 'divisible_par_9';

// On définit quelques options :
// - le répertoire où seront stockés les fichiers de cache
// - la durée de vie du cache (ici 30 secondes)
$options1 = array(
  'cacheDir' => '/tmp/',
  'lifeTime' => 30
  );

$options2 = array(
  'cacheDir' => '/tmp/',
  'lifeTime' => 45
  );

// On crée deux objets Cache_Lite_Output avec les bonnes options
$Cache_Lite_Output1 = new Cache_Lite_Output($options1);
$Cache_Lite_Output2 = new Cache_Lite_Output($options2);

// Gestion du bloc 1
if (!($Cache_Lite_Output1->start($id1))) {
  for($i = 0 ; $i<10000 ; $i++) {
    if (($i % 7)==0) {
      echo($i);
      echo('<br>');
      }
    }
  $Cache_Lite_Output1->end();
  }

// Attention, partie dynamique non cachée :
echo(date('H:i:s'));
echo('<br>');

// Gestion du bloc 2
if (!($Cache_Lite_Output2->start($id2))) {
  for($i = 0 ; $i<10000 ; $i++) {
    if (($i % 9)==0) {
      echo($i);
      echo('<br>');
      }
    }
  $Cache_Lite_Output2->end();
  }
?>

Forums
* Discutez en sur les forums

Vous voyez, c'est très simple. Cela l'aurait été encore plus si les mêmes options avaient été fixées pour les deux blocs. On aurait ainsi pu utiliser un seul objet Cache_Lite_Output. Quant aux performances, elles tombent à 130 requêtes par seconde, ce qui reste tout de même très bon.

Page 1 | 2 | 3 | 4

 
[ Fabien MartyDirection|PHP pour JDNet
 
Accueil | Haut de page