Après avoir eu un (certain) nombre de problèmes à tenter de configurer le failover Memcached dans un script PHP, j'ai pensé qu'il serait utile d'écrire mon expérience et les solutions.
Versions
Tout d'abord, il est important de préciser que je tourne sur Debian stable, donc à l'heure où j'écris ces lignes :
- Memcached: 1.4.13
- libmemcached: 1.0.8
- PHP: 5.4.4
- Memcached extension for PHP: 2.0.1
Premier test
Je vais utiliser une variable «$servers» par défaut, qui doit représenter tous vos serveurs, par exemple :$servers = array(
array('host1', 11211),
array('host2', 11211));
Note importante : vous devez toujours utiliser le même ordre pour vos serveurs Memcached, sans supprimer les serveurs morts (DEAD) : sinon votre distribution de clé (hash) changera d'un script à l'autre, et vous ne pourrez jamais atteindre de hautes performances avec Memcached. Donc configurez toujours le même tableau, dans tous les scripts où vous l'utiliserez.
Après lecture de divers publications, j'ai testé la configuration suivante :$memcached = new Memcached();
$memcached->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
$memcached->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
$memcached->addServers($servers);
Remarquez que «addServers()» doit toujours être appelé après que toutes les options soit configurées, sinon les options ne seront pas appliquées à ces serveurs.
Avec cette configuration, un appel à $memcached->set() ne fonctionnera pas sur le serveur mort (DEAD), et retourne toujours une erreur. Vous devriez d'ailleurs savoir que OPT_LIBKETAMA_COMPATIBLE change la valeur de OPT_DISTRIBUTION, de fait cette dernière option était inutile.
Activer le failover
Après bien d'autres lectures, je suis arrivé à ces options :$memcached->setOption(Memcached::OPT_SERVER_FAILURE_LIMIT, 5);
$memcached->setOption(Memcached::OPT_REMOVE_FAILED_SERVERS, 5);
La première est en fait obsolète depuis la version 0.48 de libmemcached (attention, il y a 2 déclarations de la variable sur la page, la seconde montre l'obsolescence).
La seconde option est celle qu'il vous faut. Mais j'ai trouvé son comportement étrange, car l'utilisation d'un entier ne fonctionnait pas bien :
- «0» : retournait une erreur
- «1» : activait le failover après 2 erreurs
- «2» ou plus : le failover ne fonctionnait plus
Finalement, j'ai trouvé l'information sur la page de documentation de libmemcached : c'est une valeur booléenne, soit «true» soit «false». Quando vous l'activez, par défaut, le failover est actif dès qu'il y a 2 erreurs (de connexion).
Je pense que les «2 erreurs» peuvent être configurées dans libmemcached, mais il n'y a pas de setOption() pour le moment.
Configuration finale
Voici ce que j'ai configuré sur mes serveurs :$memcached = new Memcached();
$memcached->setOption(Memcached::OPT_CONNECT_TIMEOUT, 10);
$memcached->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
$memcached->setOption(Memcached::OPT_SERVER_FAILURE_LIMIT, 2);
$memcached->setOption(Memcached::OPT_REMOVE_FAILED_SERVERS, true);
$memcached->setOption(Memcached::OPT_RETRY_TIMEOUT, 1);
$memcached->addServers($servers);
Memcached::OPT_DISTRIBUTION: configuré pour du hash consistant. Si un serveur Memcached est DEAD, ses clés de cache (et seulement ses clés) seront distribuées globalement sur les autres serveurs. C'est ici que la magie opère. C'est très différent de supprimer un serveur dans l'appel ->addServers().
Memcached::OPT_SERVER_FAILURE_LIMIT: nombre de tentatives de connexion avant qu'un serveur ne soit marqué DEAD, et supprimé de la liste des serveurs (par défaut : 5).
Memcached::OPT_REMOVE_FAILED_SERVERS: configuré à «true», pour activer la suppression des serveurs DEAD.
Memcached::OPT_RETRY_TIMEOUT: une fois qu'un serveur est déclaré DEAD, libmemcached le testera à nouveau après cette durée en secondes. Ici j'ai mis 1 seconde, mais je travaille principalement sur des scripts qui durent moins de 100ms. De fait, c'est utile uniquement sur les scripts qui tournent via un cron ou un démon.
Memcached::OPT_CONNECT_TIMEOUT: c'est le délai au bout duquel un serveur est considéré DEAD. Comme mes serveurs sont sur un même LAN, le ping est de ~0.5ms, donc 10ms est assez large pour considérer que le serveur est DEAD. Notez bien que vous devez attendre deux fois cette durée pour qu'un serveur soit marqué DEAD, donc si c'est 1000ms, votre script sera bloqué 2 secondes avant d'ignorer le serveur DEAD. Cela peut impacter fortement vos temps de réponse, c'est pourquoi je l'ai configuré très bas.
Photo par Bengt Nyman.
Aucun commentaire pour le moment