Performance & optimisations
Ce document répond à : "Y a-t-il des optimisations sur le serveur Telaria ?" Chaque optimisation est documentée avec son impact mesurable et ses piÚges.
1. OPcache PHP â bytecode en mĂ©moire
Sans OPcache, PHP relit et recompile chaque fichier .php Ă chaque requĂȘte. L'OPcache mĂ©morise le bytecode compilĂ© en RAM.
Configuration de production
; /etc/php/8.4/fpm/conf.d/10-opcache.ini opcache.enable = 1 opcache.memory_consumption = 256 ; Mo de RAM allouĂ©s au cache opcache.interned_strings_buffer = 16 ; Mo pour les chaĂźnes internalisĂ©es opcache.max_accelerated_files = 10000 ; nb max de fichiers en cache opcache.revalidate_freq = 0 ; ne jamais revĂ©rifier les fichiers opcache.validate_timestamps = 0 ; â ïž voir piĂšge ci-dessous opcache.save_comments = 1 ; requis par Doctrine (annotations) opcache.enable_cli = 0 ; pas d'OPcache en CLI (confus avec prod)
â ïž Le piĂšge OPcache en production
validate_timestamps=0 signifie que PHP-FPM ne vĂ©rifiera jamais si les fichiers ont changĂ© sur disque. C'est la bonne configuration pour la performance â mais elle exige un protocole strict lors des dĂ©ploiements.
SymptĂŽme classique : aprĂšs un git pull + migration Doctrine, le CLI (php bin/console) voit le nouveau schĂ©ma, mais les requĂȘtes web retournent SQLSTATE[42S22] Unknown column 't0.username' â PHP-FPM sert l'ancien code compilĂ©.
Solution unique : sudo systemctl reload php8.5-fpm aprĂšs chaque dĂ©ploiement. Le reload est graceful (les requĂȘtes en cours se terminent, les workers suivants chargent le nouveau code).
Vérifier l'état de l'OPcache
php -r "print_r(opcache_get_status());" | head -30 # ou depuis une URL admin si configuré
2. Brotli + Gzip â compression des rĂ©ponses
La compression rĂ©duit le volume transfĂ©rĂ© (HTML, JSON, CSS, JS) â gain typique de 60â80%.
Brotli est l'algorithme moderne (meilleure compression que Gzip, supporté par tous les navigateurs modernes). Gzip reste le fallback pour les clients anciens.
; /etc/apache2/conf-available/compression.conf <IfModule mod_brotli.c> AddOutputFilterByType BROTLI_COMPRESS \ text/html text/plain text/css text/xml text/javascript \ application/javascript application/json application/xml \ image/svg+xml font/woff2 BrotliCompressionQuality 5 ; Niveau 5 : meilleur ratio compression/CPU. Niveau 11 = max mais CPU élevé. </IfModule> <IfModule mod_deflate.c> AddOutputFilterByType DEFLATE \ text/html text/plain text/css text/xml text/javascript \ application/javascript application/json application/xml \ image/svg+xml </IfModule>
sudo a2enconf compression sudo systemctl reload apache2 # VĂ©rifier curl -sI -H "Accept-Encoding: br" https://telaria.dev | grep -i content-encoding # â content-encoding: br curl -sI -H "Accept-Encoding: gzip" https://telaria.dev | grep -i content-encoding # â content-encoding: gzip
3. HTTP/2 â multiplexage des requĂȘtes
HTTP/2 permet d'envoyer plusieurs requĂȘtes en parallĂšle sur une seule connexion TCP. Sur une page avec beaucoup d'assets (CSS, JS, images), l'impact est significatif.
; Dans le vhost HTTPS Protocols h2 http/1.1 ; h2 = HTTP/2 chiffré (requis avec HTTPS) ; http/1.1 = fallback si le client ne supporte pas HTTP/2
curl -I --http2 https://telaria.dev | head -5 # HTTP/2 200
HTTP/2 n'est disponible qu'avec HTTPS (les navigateurs ne l'implémentent qu'en mode chiffré).
4. Cache navigateur â assets versionnĂ©s
Les assets statiques (CSS, JS, images) changent rarement mais sont téléchargés à chaque visite si non cachés.
Stratégie : max-age long + versioning via AssetMapper
Symfony AssetMapper ajoute automatiquement un hash de contenu dans l'URL des assets (app.abc123.css). Un changement de fichier = nouvelle URL = cache invalidé automatiquement.
; Cache 1 an pour les assets versionnés (URL contient hash) <IfModule mod_headers.c> <FilesMatch "\.(css|js|svg|woff2|ico|png|jpg|webp)$"> Header set Cache-Control "public, max-age=31536000, immutable" </FilesMatch> </IfModule>
immutable indique au navigateur de ne jamais revalider l'asset (mĂȘme si l'utilisateur force un refresh) â l'URL a changĂ© si le contenu a changĂ©.
Vérification
# VĂ©rifier le header Cache-Control sur un asset curl -sI https://telaria.dev/assets/app.abc123.css | grep -i cache-control # â Cache-Control: public, max-age=31536000, immutable # VĂ©rifier le 304 (pas de re-tĂ©lĂ©chargement) ETAG=$(curl -sI https://telaria.dev/assets/app.abc123.css | awk '/^etag:/{print $2}') curl -I -H "If-None-Match: $ETAG" https://telaria.dev/assets/app.abc123.css # â HTTP/2 304
5. PHP-FPM pool tuning
Le pool PHP-FPM détermine combien de processus PHP tournent en parallÚle.
; /etc/php/8.4/fpm/pool.d/www.conf pm = dynamic pm.max_children = 20 ; max processus simultanĂ©s (RAM disponible / RAM par worker) pm.start_servers = 4 ; processus au dĂ©marrage pm.min_spare_servers = 2 ; processus idle minimum pm.max_spare_servers = 6 ; processus idle maximum pm.max_requests = 500 ; recycler un worker aprĂšs N requĂȘtes (prĂ©vient les fuites mĂ©moire) ; Timeout (protĂšge contre les scripts trop longs) request_terminate_timeout = 60
Calculer pm.max_children
# Mémoire par worker PHP en production ps -eo pid,rss,comm | grep php-fpm | awk '{sum+=$2} END {print sum/NR/1024 " Mo par worker"}' # Exemple : 40 Mo par worker # RAM disponible pour PHP : 1200 Mo - 400 Mo (OS+MySQL+Apache) = 800 Mo # pm.max_children = 800 / 40 = 20
Surveiller les processus FPM
# Statut du pool (activer pm.status_path dans www.conf) sudo curl --unix-socket /run/php/php8.5-fpm.sock http://localhost/fpm-status
6. MySQL â optimisations InnoDB
; /etc/mysql/mysql.conf.d/mysqld.cnf [mysqld] ; Buffer pool InnoDB (principal levier de performance MySQL) innodb_buffer_pool_size = 256M ; 256 Mo sur VPS 2 Go (adapter selon la RAM) ; Log I/O innodb_log_file_size = 64M innodb_flush_log_at_trx_commit = 2 ; Performance (perte max 1s de transactions en cas de crash) ; Connexions max_connections = 50 ; sur VPS léger, éviter les connexions fantÎmes connect_timeout = 10 wait_timeout = 300
7. Vue d'ensemble â gains attendus
| Optimisation | Gain typique |
|---|---|
| OPcache | Ă5â10 en temps d'exĂ©cution PHP |
| Brotli niveau 5 | â60â70% sur HTML/JSON vs non compressĂ© |
| HTTP/2 multiplexage | â20â40% temps de chargement page avec nombreux assets |
| Cache navigateur (max-age=1an) | 0 requĂȘte rĂ©seau pour les assets lors des visites suivantes |
| PHP-FPM tuning | Adapté à la RAM, pas de saturation ni de swap |
Ătape suivante : 07-email-delivrabilite.md