Crash Course sur les performances PHP, partie 2: Plongée en eaux profondes

Dans mon premier article dans cette série, j’ai parlé de quelques conseils de base pour optimiser la performance des applications en PHP. Dans cet article, nous allons plonger dans des eaux plus profondes vers les conseils principaux et pratiques concernant l’évolutivité de PHP.

Les institutions d’ingénierie au top ne pensent pas que la performance est quelque chose d’agréable à avoir, elles pensent qu’il s’agit d’une caractéristique cruciale de leur produit. Ces institutions comprennent que la performance a un impact direct sur le succès de leur business.

Finalement, l’évolutivité concerne l’architecture dans son ensemble, pas seulement quelques optimisations mineures de code. Les gens le comprennent souvent mal et pensent naïvement qu’ils doivent se concentrer sur les « bords ». Des solides décisions architecturales – comme effectuer un travail de blocking en background en utilisant des tâches, attraper de manière proactive des calls onéreux et utiliser un cache proxy inversé – vont vous amener bien plus loin que le simple fait de se disputer à propos de simple ou double guillemet.

Voici quelques principes essentiels à garder en tête pour des applications PHP performantes:

Ces quelques premiers conseils ne requièrent pas vraiment une quelconque élaboration, je vais donc me concentrer sur ce qui est important.

Optimisez vos sessions

Sur PHP il est très facile de déplacer votre magasin de session vers Memcached:

1. Installez l’extension Memcached avec PECL

pecl install memcached

2. Personnalisez votre configuration php.ini pour changer le responsable de session

session.save_handler = memcached
session.save_path = "localhost:11211"

Si vous souhaitez accepter un pool de demandes memache, vous pouvez séparez avec une virgule:

session.save_handler = memcached
session.save_path = "10.0.0.10:11211,10.0.0.11:11211,10.0.0.12:11211"

L’extension Memcached possède une palette d’options de configuration disponible, vous pouvez voir la liste complète sur Github.

La configuration idéale que j’ai trouvé en utilisant un pool de serveurs:

session.save_handler = memcached
session.save_path = "10.0.0.10:11211,10.0.0.11:11211,10.0.0.12:11211"

memcached.sess_prefix = « session. »
memcached.sess_consistent_hash = On
memcached.sess_remove_failed = 1
memcached.sess_number_of_replicas = 2
memcached.sess_binary = On
memcached.sess_randomize_replica_read = On
memcached.sess_locking = On
memcached.sess_connect_timeout = 200
memcached.serializer = « igbinary »

Et voilà! Vous pouvez consulter la documentation pour une explication complète des ces directives de configuration.

Mettre en place le Caching

Chaque donnée qui est onéreuse à générer ou à demander et qui a une longue durée de vie devrait être mise en cache dans la mémoire interne si possible. Les exemples communs de donnée devant être mises en cache incluent des réponses de service web, des ensembles de résultat de bases de données et des données de configuration.

Utiliser la composante Symfony2 HttpFoundation pour construire un support de cache http

Je ne vais pas essayer d’expliquer le caching http. Allez seulement lire le fantastique article de Ryan Tomako, Things Caches Do ou le in-depth guide to http caching de mark Nottingham. Les deux articles sont brillants et chaque développeur professionnel devrait les lire.

Avec la composante Symfony2 HttpFoundation, il est aisé d’ajouter un support pour mettre en cache vos réponses http. La composante est complètement autonome et peut être mise dans chaque application php existante pour fournir une abstraction orientée objet autour de la spécification http. L’objectif est d’aider à gérer les requêtes, réponses et sessions. Ajoutez « symfony/http-foundation » à votre fichier Composer et vous êtes prêt à commencer.

Expires based http caching flow

use SymfonyComponentHttpFoundationResponse;

$response = new Response(‘Hello World!’, 200, array(‘content-type’ => ‘text/html’));

$response->setCache(array(
‘etag’ => ‘a_unique_id_for_this_resource’,
‘last_modified’ => new DateTime(),
‘max_age’ => 600,
‘s_maxage’ => 600,
‘private’ => false,
‘public’ => true,
));

Si vous utilisez requête et réponse depuis la fondation http, vous pouvez contrôler vos validateurs conditionnels facilement à partir de la requête:

use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationResponse;

$request = Request::createFromGlobals();

$response = new Response(‘Hello World!’, 200, array(‘content-type’ => ‘text/html’));

if ($response->isNotModified($request)) {
$response->send();
}

Retrouvez plus d’exemples et une documentation complète sur la très détaillée Symfony documentation.

Caching de l’ensemble de résultats avec Doctrine ORM

Si vous n’utilisez pas un ORM ou une forme d’abstraction de base de données, vous devriez prendre ceci en compte. Doctrine est la couche d’abstraction de base de données et le mappeur relationnel-objet le plus complet disponible pour PHP. Bien sûr, l’ajout d’abstractions est lié au coût de la performance, mais je trouve que Doctrine est extrêmement rapide et efficace si on l’utilise correctement. Si vous mettez à profit le Doctrine ORM, vous pouvez facilement activer le caching des ensembles de résultats sur Memcached:

$memcache = new Memcache();
$memcache->connect('localhost', 11211);

$memcacheDriver = new DoctrineCommonCacheMemcacheCache();
$memcacheDriver->setMemcache($memcache);

$config = new DoctrineORMConfiguration();
$config->setQueryCacheImpl($memcacheDriver);
$config->setMetadataCacheImpl($memcacheDriver);
$config->setResultCacheImpl($memcacheDriver);

$entityManager = DoctrineORMEntityManager::create(array(‘driver’ => ‘pdo_sqlite’, ‘path’ => __DIR__ . ‘/db.sqlite’), $config);

$query = $em->createQuery(‘select u from EntitiesUser u’);
$query->useResultCache(true, 60);

$users = $query->getResult();

Vous trouverez plus d’exemples et une documentation très détaillée sur Doctrine documentation.

Caching de Réponses de service web avec Guzzle http client

Interagir avec des services web est très ordinaire dans les applications web modernes. Guzzle est le client http disponible pour PHP le plus complet. Guzzle se charge d’envoyer des requêtes http et il prend également en charge la redondance provenant des clients de création de service web. Il s’agit d’un framework qui inclut les outils nécessaires pour créer un client robuste de service web. Ajoutez « guzzle/guzzle » à votre fichier Composer et vous êtes prêt à débuter.

Non seulement Guzzle supporte de nombreuses méthodes d’authentification (OAuth 1+2, http Basic, etc), mais il supporte également les meilleures pratiques comme les retries with exponential backoffs aussi bien que le http caching.

$memcache = new Memcache();
$memcache->connect('localhost', 11211);

$memcacheDriver = new DoctrineCommonCacheMemcacheCache();
$memcacheDriver->setMemcache($memcache);

$client = new GuzzleHttpClient(‘http://www.test.com/’);

$cachePlugin = new GuzzlePluginCacheCachePlugin(array(
‘storage’ => new GuzzlePluginCacheDefaultCacheStorage(
new GuzzleCacheDoctrineCacheAdapter($memcacheDriver)
)
));
$client->addSubscriber($cachePlugin);

$response = $client->get(‘http://www.wikipedia.org/’)->send();

// response will come from cache if server sends 304 not-modified
$response = $client->get(‘http://www.wikipedia.org/’)->send();

Si vous suivez ces conseils, cela vous permettra de mettre facilement en cache toutes vos requêtes de base de données, requêtes de service web et réponses http.

Déplacer le travail dans le background avec Resque et Redis

Chaque processus qui est lent et qui n’est pas important pour la réponse immédiate http devrait être mis en file et effectué par les tâches non-blocking du background. Des exemples ordinaires sont les notifications envoyées sur les réseaux sociaux (comme Facebook, Twitter, LinkedIn), les emails envoyés et les statistiques de processus. Il y a beaucoup de systèmes disponibles pour la gestion des couches de messages et les files de tâches, mais je trouve Resque for PHP très simple. Je ne vais pas proposer un guide complet car Wan Qi Chen a déjà publié une excellente série d’articles de blog sur comment débuter avec Resque. Ajoutez « chrisboulton/php-resque » à votre fichier Composer et vous êtes prêt à démarrer. Voici une très simple introduction sur la manière d’ajouter Resque à votre application:

1) Définissez un backend Redis

Resque::setBackend('localhost:6379');

2) Définissez une tâche de background

class MyTask
{
public function perform()
{
// Work work work
echo $this->args['name'];
}
}

3) Ajoutez une tâche à la file

Resque::enqueue('default', 'MyTask', array('name' => 'AppD'));

4) Faites démarrer une tâche de ligne de commande pour effectuer les tâches avec cinq workers de la queue jusqu’au background.

$ QUEUE=* COUNT=5 bin/resque

Pour plus d’informations, lisez la documentation officielle ou jetez un œil au tuto très complet de Wan Qi Chen:

  • Part 1 : Introduction
  • Part 2 : Queue system
  • Part 3 : Installation
  • Part 4 : Worker
  • Part 5 : Job class and implementation
  • Part 6 : Integrate Resque into CakePHP with CakeResque
  • Part 7 : Start and stop workers with Fresque
  • Part 8 : A look into php-resque-ex, a fork with more features
  • Part 9 : Resque analytics with ResqueBoard

Suivez la performance de production

AppDynamics est un logiciel de gestion de performance de l’application créé pour aider les devs et les ops à dépanner les problèmes de performance dans des applications complexes de production. Le chemin du flux d’application vous permet de suivre les appels vers la base de données, les caches, les files et les services web avec un détail de niveau code, jusqu’au problèmes de performance:

Prenez cinq minutes aujourd’hui pour avoir la complète visibilité de la performance de vos applications de production avec AppDynamics Pro.

Si vous préférez le format slide, ces articles sont tirés d’une récente présentation tech que j’ai effectuée:

Comme toujours, n’hésitez pas à laisser un commentaire si vous estimez que j’ai oublié quelque chose ou si vous avez une requête de contenu pour un article à venir.