Raccourcis : Contenu - rubriques - sous rubriques
EN FR

Mettre en cache des données ou des portions de page HTML générées permet d'améliorer les performances du site web, voire même d'éviter de transférer des données au niveau navigateur.

Jelix propose plusieurs systèmes de cache. Vous avez probablement vu la mise en cache des zones. Vous allez voir ici la mise en cache de données quelconques, et l'utilisation du cache HTTP.

jCache

jCache est une classe permettant de stocker des données dans un système de cache. Le système repose sur des pilotes pour les accès aux données. Il est donc possible d’utiliser memcache, redis, ou encore une base de données, voire même le système de fichiers. Et vous pouvez développer votre propre pilote.

Configuration

Pour utiliser jCache, il faut d'abord spécifier les paramètres de configuration du système dans le fichier profiles.ini.php situé dans var/config/. Voir le chapitre sur l'utilisation de ce fichier profiles.ini.php.

Vous pouvez définir plusieurs configurations du système, que l'on nomme “profils”. Ainsi vous pouvez définir des configurations pour le site de production, le site de développement par exemple.

Le type de connexion à indiquer dans les noms des sections de profiles.ini.php est jcache.

Chaque profil doit indiquer au moins ces trois paramètres :

  • driver : indique le pilote à utiliser.
  • enabled : permet d'indiquer si le cache pour ce profil est actif (1) ou non (0). Utile en développement ou débuggage.
  • ttl : indique le temps d’expiration des données (0 signifie n’expire jamais). Vous pouvez indiquer une durée en secondes, un timestamp UNIX ou encore une date textuelle au format US.
  • automatic_cleaning_factor : indice de nettoyage automatique du système de cache.

Un profil peut contenir d'autres paramètres, en fonction du pilote utilisé.

Voici un exemple dans le fichier profiles.ini.php :


[jcache]
default = foo

[jcache:foo]
enabled = 1
driver = file
; autres paramètres...

Pilote memcache

Il utilise l'API memcache de PHP (et non pas l'API memcached).

Paramètres possibles :

  • driver=memcache
  • servers : liste de serveurs memcached.

[jcache:mymemcache]
driver=memcache
ttl=360
enabled=1
servers = memcache_host1:11211,memcache_host2:11211,memcache_host3:11211

Pilote Redis

Les données sont stockées dans une base Redis.

Deux plugins sont disponibles :

  • "redis_ext", fourni avec Jelix, se base sur l'API de l'extension PHP redis (plugin introduit dans Jelix 1.6.14)
  • "redis_php", disponible séparément via le paquet Composer "jelix/php-redis-plugin", utilise une classe pure PHP (PhpRedis) pour communiquer avec la base Redis. (plugin introduit dans Jelix 1.6.8 sous le nom "redis" et renommé en "redis_php" dans la version 1.6.14)

Paramètres possibles :

  • host : nom ou ip du serveur redis
  • port : le port réseau sur lequel se connecter
  • db : le numéro de base de donnée redis (0 par défaut)
  • key_prefix : un prefix qui sera ajouté sur chaque clé indiquée
  • key_prefix_flush_method : méthode de suppression des clés quand on indique un prefixe.

Exemple :


[jcache:myredis]
driver=redis_ext
ttl=360
enabled=1
host=192.168.0.1
port=6379
db=0
key_prefix=
key_prefix_flush_method=

Si vous indiquez un prefix pour les clés, alors toutes les clés que vous indiquez seront préfixées par le prefix en question dans la base Redis.

De même, le vidage du cache ne concernera que les clés commençant par ce prefix. Cependant, à cause de l'implementation d'une telle fonction dans Redis, cela peut être très gourmand en temps, et rendre votre application trop lente.

Diverses méthodes sont implémentées dans le plugin pour supprimer les clés, et sont indiquées dans le paramètre key_prefix_flush_method :

  • direct : les clés sont supprimées directement. Comportement par défaut. Mais à n'utiliser que si vous savez que votre base Redis ne contient pas beaucoup de clés.
  • jcacheredisworker : la suppression des clés sera faite de manière asynchrone par un worker ou un cron.
  • event : si les deux autres méthodes ne vous conviennent pas, il est alors de votre responsabilité d'implémenter le processus (asynchrone de préférence !) qui vous convient. Un évènement jCacheRedisFlushKeyPrefix est émis par le plugin, avec le nom du profile et le prefixe des clés à supprimer. à vous d'implémenter un listener sur cet évènement qui appellera votre processus de suppression.

Fonctionnement de la suppression asynchrone des clés à prefixes :

Le plugin stocke les prefixes des clés à supprimer dans une liste qui a pour clé jcacheredisdelkeys dans Redis. Un worker ou un cron doit alors dépiler régulièrement cette liste de préfix et supprimer les clés correspondantes.

Vous avez un exemple de worker dans lib/jelix/core-modules/jelix/Command/RedisCacheDeletionWorker.php. Vous pouvez le lancer au moyen de systemd/sysinit/supervisord par exemple. La commande à lancer est php monappli/console.php jcache:redis:delete <profile><profile> est le nom du profile jcache concerné. La commande scrute en permanence la liste jcacheredisdelkeys dans Redis et efface les clés correspondantes aux prefixes de cette liste. Pour l'arrêter, un <CTRL+C> (sur linux) suffira si vous le lancez vous même en ligne de commande.

Pilote file

Le cache est stocké dans des fichiers.

Paramètres possibles :

  • driver=file
  • cache_dir : le répertoire où les données sont stockées.
  • file_locking : indique s’il y a verrouillage des fichiers (1) ou non (0).
  • directory_level : niveau d’arborescence des répertoires du système de cache.
  • directory_umask : attributs utilisés pour les répertoires.
  • file_name_prefix : préfixe des noms de fichiers du système de cache. Depuis Jelix 1.6.8, c'est le préfixe du répertoire du cache.
  • cache_file_umask : attributs utilisés pour les fichiers.

Pilote db

Ce pilote utilise une table SQL pour stocker les valeurs de cache. L'installateur de jelix crée la table nécessaire quand l'application l'utilise. Cependant, pendant le développement, vous devez vous même créer la table. Utilisez l'un des scripts SQL dans lib/jelix/core-modules/jelix/install/sql/install_jcache.schema.* pour créer la table.

Paramètres possibles :

  • driver=db : la valeur est "db".
  • dao : un sélecteur vers le fichier dao que vous voulez utiliser. Par défaut, "jelix~jcache".
  • dbprofile : profil de connexion jDb à utiliser .

Utilisation

jCache est la classe principale du système de cache. Toutes ses méthodes sont statiques. Elle effectue un bon nombre d’opérations comme stockage, suppression, récupération de données. Vous appellerez ces méthodes quand bon vous semble.

  • jCache::get($key): Pour récupérer une valeur. Retourne false si la clé n'existe pas
  • jCache::set($key, $value, $ttl): pour stocker ou modifier une valeur. $ttl est optionnel
  • jCache::delete($key): pour effacer une valeur
  • jCache::increment($key): incremente une valeur (N.B. : la valeur est réduite à sa partie entière - si elle a une partie décimale - puis incrémentée)
  • jCache::decrement($key): decremente une valeur (N.B. : la valeur est réduite à sa partie entière - si elle a une partie décimale - puis décrémentée)
  • jCache::add($key, $value): pour stocker une nouvelle valeur. Retourne false si la clé existe déjà
  • jCache::replace($key,$value, $ttl): pour changer une valeur d'une clé existante. Retourne false si la clé n'existe pas. $ttl est optionnel.
  • jCache::garbage(): efface toutes les valeurs expirées
  • jCache::flush(): efface tout le cache.
  • jCache::call($fn, $fnargs, $ttl): utilise le nom et les arguments de la fonction donnée comme clé de la valeur que retourne la fonction. Si la clé existe déjà, renvoi la valeur du cache, sinon appelle la fonction et stocke la valeur retournée dans le cache.

Toutes ces méthodes statiques acceptent un nom de profile jcache en dernier argument.

Les noms des clés sont restreints aux caractères suivants : lettres alphabets, chiffres, /, _, -, :, ..

jResponse et le cache HTTP

Le protocole HTTP possède des en-têtes pour informer le navigateur comment stocker la page qu'on lui envoi, c'est à dire si le navigateur peut la mettre en cache ou pas. Et le navigateur peut, lors d'une requête, indiquer au serveur qu'il a ou pas la page demandée dans son cache, et depuis quand. Coté serveur, on peut donc savoir si le contenu demandé est dans le cache du navigateur, et si il est périmé ou pas, ce qui permet de générer ou pas le contenu.

La classe jResponse possède des méthodes pour gérer facilement le cache HTTP d’une réponse en exploitant les entête prévus à cet effet dans le protocole HTTP.

Ces méthodes sont disponibles dans toutes les réponses pour lesquelles l'utilisation du cache HTTP a du sens. N’oubliez pas que ce cache se situe côté client, dans le navigateur de l’utilisateur et non sur le serveur.

Il y a deux façons de gérer le cache coté navigateur : le cache par expiration, et le cache par validation.

Attention : en général, sauf à utiliser la méthode setLifeTime avec un cache privé, la mise en cache ne doit pas concerner des pages "personnalisées" incluant par exemple des données relatifs à l'utilisateur. En effet, le contenu est susceptible d'être mis en cache par des proxys. Ceux-ci utilisent les en-têtes de cache envoyés par le serveur pour stocker en cache (ou pas), mais n'utilisent pas les cookies (notamment ceux de sessions) comme paramètres discriminants : ils ne mémorisent donc qu'une seule "version" d'une page et tout les utilisateurs obtiendront cette version de page. Si elle contient des données personnelles, cela peut avoir des conséquences dramatiques.

Cache par expiration

Le cache par expiration est un cache qui restera valide jusqu'à une certaine date. Avant cette date, le navigateur ne fera pas de demande au serveur, et affichera directement la page qu'il a en cache. Par contre, au delà de cette date, le navigateur redemandera la page au serveur. Vous pouvez spécifier cette date d'expiration soit en indiquant la date proprement dite (méthode setExpires), soit en indiquant une durée (méthode setLifeTime).

setExpires

La méthode setExpires permet de spécifier une date jusqu'à laquelle la ressource sera considérée valide et gardée en cache. En interne, cette méthode ajoute un entête HTTP “Expires”. Dans le contrôleur :


$rep->setExpires($date);

//ou bien

$rep->setExpires(“+1 days”);

Le paramètre attendu par setExpires est une date de la forme jDateTime, DateTime ou une chaine compatible avec la fonction strtotime.

setLifeTime

La méthode setLifeTime permet de spécifier un temps (en secondes) pendant lequel la ressource sera considérée valide et gardée en cache.

Vous avez également la possibilité de spécifier si la ressource peut être mise en cache par n’importe quel cache ou si elle doit être privée et ne pas être mise en cache par des proxys (par défaut : privée).

En interne, cette méthode ajoute un entête HTTP “Cache-Control”.

Dans le contrôleur :


$rep->setLifeTime(3600); //1 heure en cache privé

//ou bien

$rep->setLifeTime(600, true);// 10 minutes en cache partagé

Cache par validation

Le cache par validation permet d'utiliser une politique de mise en cache plus complexe : le navigateur demande systèmatiquement la page au serveur, en lui indiquant une information de validité que lui avait donné le serveur. Mais le serveur, en fonction de cette date, peut décider de renvoyer une nouvelle page, ou de dire au navigateur d'utiliser celle qu'il a en cache.

C'est donc à vous, dans votre contrôleur, de décider si il faut renvoyer ou non un nouveau contenu. Pour cela, vous indiquez explicitement à la méthode isValidCache, soit la dernière date à laquelle la ressource a été modifiée ("Last-Modified"), soit un token correspondant à l'etat de la ressource ("Etag"). À vous donc de récupérer cette date (par exemple, si c'est une page d'un article, la date de modification de cet article), ou de calculer le token (qui peut être un checksum md5 par exemple, ou une chaine identifiant de façon unique la version de la page..).

La méthode isValidCache, retournera true si la ressource du navigateur est encore bonne, ou false si il faut regénérer la page.

Dans le contrôleur :


$dateLastModified = $foo->updated_at; //date de dernière modification de ma ressource

if ($rep->isValidCache($dateLastModified)) {
   // le cache est bon, on arrête là
   return $rep;
}

//Ou bien en utilisant “Etag”;

$etag = ... ; //je génère un etag correspondant à l’etat de ma ressource

if ($rep->isValidCache(null, $etag)) {
   return $rep;
}

Le paramètre date attendu par isValidCache est une date de la forme : jDateTime, DateTime ou une chaine compatible avec la fonction strtotime.