La mise en file d'attente des requêtes PHP limite le nombre de requêtes traitées simultanément par ton serveur et détermine ainsi le temps de réponse, le taux d'erreur et l'expérience utilisateur. Je vais te montrer comment Limites de traitement Le but est d'éliminer les goulets d'étranglement et d'obtenir une livraison constante grâce à des paramètres harmonisés.
Points centraux
Pour que tu puisses commencer tout de suite, je résume les principales vis de réglage pour PHP-FPM ensemble.
- pm.max_children: calculer la limite supérieure des processus PHP simultanés, en fonction de la RAM.
- listen.backlog: Maximiser la mise en mémoire tampon à court terme des tentatives de connexion lors des pics de charge.
- pm.max_requests: recycler régulièrement les processus afin d'éviter les fuites de mémoire et le gonflement.
- Timeouts: définir de manière cohérente request_terminate_timeout, max_execution_time et les timeouts du serveur web.
- Métriques: max children reached, listen queue et slowlogs à vérifier en permanence.
Je mise sur des indicateurs clairs et des effets mesurables pour que chaque adaptation à des Limites reste compréhensible. Pour chaque modification, j'observe les logs et les temps de réponse avant de planifier la prochaine étape et d'augmenter ou de diminuer progressivement les valeurs. J'évite ainsi des effets secondaires tels que le memory swapping, qui peut affecter chaque Queue se prolonge de manière spectaculaire. En procédant de la sorte, je ramène les pics de charge à des proportions plus raisonnables et maintiens les temps de réponse à un niveau stable. L'objectif est d'obtenir une charge de travail équilibrée, qui Ressources sans surcharger l'hôte.
Comment la mise en file d'attente des requêtes PHP fonctionne dans PHP-FPM
Chaque requête HTTP entrante nécessite un Travailleur, et un worker ne traite qu'une seule demande à la fois. Si tous les processus sont occupés, d'autres appels atterrissent dans la Queue et attendre qu'un processus se libère. Si cette file d'attente augmente, les temps de réponse s'allongent et des erreurs comme 502/504 se produisent plus souvent. Je veille donc à un rapport raisonnable entre le nombre de processus et la mémoire disponible, plutôt que de miser aveuglément sur un parallélisme maximal. J'obtiens ainsi un débit constant sans que RAM ou de l'unité centrale.
Choisir proprement les modes du gestionnaire de processus
Outre les valeurs limites, c'est le mode pm sur la réactivité et la consommation de ressources :
- pm = dynamiqueJe définis start_servers, min_spare_servers et max_spare_servers. Ce mode est mon standard pour les charges variables, parce qu'il réagit rapidement aux montées et tient des processus chauds à disposition.
- pm = ondemand: Les processus ne sont créés qu'en cas de besoin et sont terminés après process_idle_timeout. Cela permet d'économiser de la RAM en cas d'accès peu fréquents (admin, staging, endpoints cron), mais peut être problématique en cas de pics soudains. démarrages à froid et générer une latence plus élevée. Je l'utilise donc de manière ciblée et avec un backlog généreux.
- pm = static: Un nombre fixe de processus. Idéal si j'ai une plafond dur et des latences particulièrement prévisibles (p. ex. proxy L7 devant quelques points finaux critiques). Les besoins en RAM sont clairement calculables, mais les processus inutilisés consomment de la mémoire.
Je décide pour chaque pool quel mode convient au profil. Pour les frontaux à charge variable, j'utilise généralement dynamic, pour les pools utilitaires ondemand et pour les services dédiés à latence critique, éventuellement static.
déterminer correctement pm.max_children
Le levier le plus important est pm.max_children, car cette valeur définit le nombre de requêtes qui peuvent être exécutées simultanément. Je calcule la taille de démarrage avec la formule empirique : (RAM disponible - 2 Go de réserve) divisé par la mémoire moyenne par processus PHP. En gros, je prévois 40-80 Mo par processus et je démarre avec 200-300 processus sur un hôte de 32 Go. Sous charge réelle, j'augmente ou je diminue progressivement et je vérifie si le temps d'attente des Queue et le taux d'erreur diminue. Pour ceux qui souhaitent aller plus loin, vous trouverez des informations sur les valeurs de départ et les valeurs limites sous Optimiser pm.max_children.
Coordonner les valeurs de départ, de réserve et de backlog
Je mets pm.start_servers à environ 15-30% de pm.max_children, afin qu'il y ait suffisamment de processus prêts au début et qu'il n'y ait pas de démarrages à froid. Avec pm.min_spare_servers et pm.max_spare_servers, je définis une fenêtre raisonnable pour les processus libres, afin que les nouvelles requêtes n'attendent pas et qu'en même temps, aucun ralenti inutile ne mobilise de la mémoire. Listen.backlog est particulièrement important : Cette mémoire tampon du noyau retient brièvement les tentatives de connexion supplémentaires lorsque tous les travailleurs sont occupés. En cas de pics de charge, je fixe des valeurs élevées (par exemple 65535) pour que les file d'attente ne s'arrête pas avant le pool FPM. Pour plus d'informations sur l'interaction entre le serveur web, le flux montant et les tampons, voir l'aperçu de Mise en file d'attente du serveur web.
Limiter les temps d'exécution des requêtes et recycler les processus
J'évite les remontées de mémoire insidieuses avec pm.max_requests, qui redémarre chaque processus après X requêtes. Les applications discrètes fonctionnent souvent bien avec 500-800, si je soupçonne des fuites de mémoire, je réduis à 100-200 et j'observe l'effet. De plus, request_terminate_timeout encapsule les dérives en mettant fin aux requêtes extrêmement longues après une durée fixe. La cohérence est importante : je maintiens le max_execution_time de PHP et les timeouts du serveur web dans le même corridor, afin qu'une couche ne s'arrête pas plus tôt que l'autre. Cette interaction maintient Travailleur et protège la piscine de la congestion.
Rendre les files d'attente visibles : Logs et métriques
Je lis régulièrement les logs FPM et je fais attention à max children reached, Cette entrée indique que la limite supérieure des processus a été atteinte. En parallèle, j'observe la file d'attente des listes, qui permet de détecter une accumulation croissante dans le tampon d'entrée. En combinaison avec request_slowlog_timeout, j'obtiens des traces de la pile aux endroits lents du code et j'isole les freins de la base de données ou de l'API. Je corrèle upstream_response_time des logs du serveur web avec request_time et les codes d'état pour limiter la source des longs temps de réponse. Je peux ainsi déterminer si le goulot d'étranglement dans PHP-FPM, le Base de données ou le réseau en amont.
Profils de charge de travail : Lié au CPU vs. lié à l'IO
Pour les processus nécessitant beaucoup de CPU, je mets à l'échelle les Parallélisme Je suis prudent et m'oriente étroitement vers le nombre de vCPU, car les processus supplémentaires n'apportent guère de débit. S'il s'agit principalement d'une charge IO avec accès à une base de données ou à des API externes, je peux autoriser plus de processus tant que le budget RAM est suffisant. Les contrôles du commerce électronique profitent de délais d'attente plus longs (par exemple 300 s) pour achever les chemins de paiement sans interruption. J'intercepte les ventes flash en augmentant la valeur de listen.backlog et en agrandissant la fenêtre de spare. Des conseils sur l'équilibre entre le nombre de processus et la performance de l'hôte sont donnés dans le guide de PHP-Workers comme goulot d'étranglement.
Exemples de calcul et de dimensionnement
Je calcule tout d'abord la mémoire par processus et j'en déduis une valeur raisonnable. Plafond de la file d'attente. Ensuite, je teste sous charge réelle et j'observe si la file d'attente diminue et si le débit augmente. Des valeurs de départ conservatrices réduisent le risque de swapping et maintiennent un temps de réaction régulier. Ensuite, j'affine par petites étapes pour être sûr de remarquer les effets secondaires. Le tableau suivant fournit des conseils sur les valeurs de départ et les effets sur la Queue.
| Paramètres | Effet | Valeur de départ (exemple) | Remarque |
|---|---|---|---|
| pm.max_children | Nombre max. d'utilisateurs simultanés Processus | 200-300 (pour 32 Go) | Comparer avec le budget RAM et la taille du processus |
| pm.start_servers | Nombre initial de travailleurs | 15-30 % de max_children | Éviter les démarrages à froid, mais maintenir le ralenti au minimum |
| pm.min_spare_servers | Libre Travailleur Minimum | z. B. 20 | Enregistrement direct de nouvelles requêtes |
| pm.max_spare_servers | Travailleur libre Maximum | z. B. 40 | Limiter l'utilisation de la RAM par les processus inertes |
| listen.backlog | Mémoire tampon du noyau pour les tentatives de connexion | 65535 | Amortir les pics de charge et réduire les pertes de connexion |
| pm.max_requests | recyclage Intervalle | 500-800, en cas de fuites 100-200 | Minimiser le gonflement de la mémoire et les accrocs |
| request_terminate_timeout | Limite de requêtes dure | 300-600 s | Cohérent avec les timeouts de PHP et du serveur web |
Modèles pratiques pour les pools PHP-FPM
Pour une boutique avec beaucoup d'accès en lecture, je mets modérément Chiffres du processus et augmente la fenêtre Spare pour que les requêtes ne soient pas en attente. Pour les pages de contenu avec mise en cache, il suffit souvent de beaucoup moins de worker, tant que NGINX ou Apache fournissent efficacement des contenus statiques. Je sépare les configurations multi-pools selon les parties de l'application qui ont des profils de mémoire différents, afin qu'aucun pool lourd ne supplante les autres. Pour les travailleurs Cron ou Queue, je définis des pools séparés avec leur propre ensemble de règles de timeout. C'est ainsi que je maintiens l'interactivité Trafic libre et ne freine pas les actions des utilisateurs.
Timeouts du serveur web, upstream et sockets
Je considère les délais FastCGI et Proxy de Nginx ou Apache dans la même fenêtre que les timeouts FPM, afin qu'aucune couche ne s'arrête trop tôt. Je préfère les sockets Unix à TCP, à condition que les deux services fonctionnent sur le même hôte, car la latence reste minimale. Pour les configurations distribuées, j'utilise TCP avec des valeurs keepalive stables et un pool de connexions suffisamment grand. Pour un parallélisme élevé, nginx coordonne les worker_connections et les valeurs de backlog FPM. Ainsi, les transferts restent rapides et j'évite les temps morts dus à des connexions trop étroites. en amont-limites.
Mise en cache, OPCache et base de données comme leviers de commande
Je résous de nombreux problèmes de serveur en réduisant les opérations coûteuses et en Temps de réponse de la mémoire. J'active l'OPCache, j'augmente judicieusement la limite de mémoire du cache et je veille à ce que le débit du cache soit élevé. Pour les résultats récurrents, j'utilise la mise en cache des applications pour que les processus PHP se terminent plus rapidement. Côté base de données, j'optimise les requêtes lentes et j'active des caches de requêtes adaptés au système utilisé. Chaque milliseconde économisée soulage les Queue et augmente le débit par travailleur.
Sécuriser les mécanismes d'urgence et les redémarrages
J'active seuil_redémarrage_d'urgence et emergency_restart_interval, afin que le maître FPM redémarre si trop d'enfants se crashent rapidement les uns après les autres. Ce redémarrage contrôlé empêche les réactions en chaîne et maintient le service disponible. Parallèlement, je fixe des limites claires pour la mémoire et le nombre de processus afin d'amortir les escalades. Les contrôles de santé en amont retirent automatiquement les backends défectueux du pool et réduisent les taux d'erreur. Ainsi, la Disponibilité pendant que j'enquête sur la cause réelle.
Ajuster finement les limites du système d'exploitation et de Systemd
Avec cela, listen.backlog s'applique effectivement, je compense les limites du noyau. La valeur OS net.core.somaxconn doit être au moins aussi élevée que le backlog défini, sinon le système coupe la file d'attente. Je vérifie également le nombre de descripteurs de fichiers autorisés : Dans le pool FPM, je peux définir rlimit_files, au niveau du service je sécurise LimitNOFILE (systemd) et au niveau du noyau fs.file-max. Le serveur web a besoin de réserves similaires pour ne pas atteindre ses limites plus tôt.
Pour des latences plus stables, je réduis vm.swappiness, afin que le noyau ne déplace pas prématurément les pages mémoire utilisées activement. Dans les configurations où la latence est critique, je désactive Pages transparentes volumineuses, pour éviter les longs défauts de page. Si FPM fonctionne via TCP, je compare également net.ipv4.tcp_max_syn_backlog et les paramètres Reuse/Keepalive. De tels détails du système d'exploitation semblent insignifiants, mais ils déterminent si les files d'attente lisse ou si les connexions sont déjà rejetées avant FPM.
Mesurer la mémoire par processus de manière résiliente
Au lieu d'estimer globalement, je mesure le Consommation réelle par travailleur sous charge réelle. J'utilise des outils comme ps, smem ou pmap, je filtre sur les enfants php-fpm et je fais la moyenne des valeurs RSS pendant que des requêtes sont en cours. Il est important de tenir compte de l'utilisation commune et partagée de l'OPCache : on ne compte pas plusieurs fois la mémoire partagée. A partir de la valeur moyenne, je déduis pm.max_children et je prévois en plus une réserve pour que la machine ne se mette pas en défaut même en cas de pics. échange bascule.
Je répète cette mesure après des changements de fonctionnalités ou de versions. De nouvelles fonctionnalités, davantage de dépendances ou des modifications de frameworks peuvent augmenter considérablement l'empreinte par processus. Ainsi, le nombre de processus reste réaliste et la file d'attente courte.
État PHP-FPM, ping et métriques vivantes
Pour une évaluation rapide de la situation, j'active pm.status_path et un Point final de ping (ping.path/ping.response). J'y vois des indicateurs comme accepted conn, listen queue len, idle/busy processes, max children reached et leur évolution. Je lis périodiquement ces valeurs et fixe des seuils : si listen queue augmente durablement, j'augmente soit les processus, soit je supprime la cause des requêtes lentes. Si max children reached se déclenche alors que idle reste bas, le pool est trop petit ou bloqué par coureur long.
De plus, je sépare les pools avec différents profils afin que les pics dans un domaine (par ex. les importations API) ne mettent pas le trafic interactif à genoux. Pour les cas de diagnostic, j'augmente temporairement le log_level et je laisse le slowlog capturer plus d'échantillons, mais je le réduis ensuite pour maintenir la charge I/O à un faible niveau.
Téléchargements, mise en mémoire tampon et gros volumes de requêtes
Les gros téléchargements peuvent mobiliser inutilement les travailleurs si PHP doit d'abord lire le corps de la requête. Je fais en sorte que le serveur web tamponne (par exemple fastcgi_request_buffering chez NGINX), de sorte que FPM ne démarre que lorsque le corps est complet. Ainsi, aucun worker ne se bloque pendant le téléchargement. Avec client_max_body_size, post_max_size et max_input_time, je contrôle la taille et la durée des requêtes sans mettre en danger les points finaux. Si des fichiers sont en attente, j'attribue une mémoire temporaire (SSD) suffisamment rapide pour éviter les bourrages dans la mémoire tampon.
Pour les points de terminaison avec de très grands corps (par ex. exportations/importations), je définis des pools dédiés avec leurs propres timeouts et un parallélisme réduit. Ainsi, les workers standard restent libres et les Queue des actions importantes des utilisateurs brièvement.
Connexions à la base de données et limites du pool
Le meilleur réglage du FPM ne sert à rien si les Base de données était auparavant limité. J'aligne le nombre maximal de processus PHP simultanés sur la capacité réelle de la base de données. Pour les connexions persistantes ou les pools de connexions, je fais en sorte que la somme de tous les pools à l'adresse suivante : max_connections reste. Si de nombreuses requêtes courtes sont créées, il est utile de limiter modérément le parallélisme PHP afin que la base de données ne soit pas thrashée entre des milliers de sessions.
Les transactions lentes provoquent rapidement un embouteillage jusque dans la file d'attente FPM. C'est pourquoi j'analyse les temps d'attente de verrouillage, l'utilisation des index et les plans de requête. Toute réduction du temps d'exécution de la base de données réduit immédiatement la durée de la requête PHP.Durée du document et réduit les longueurs de file d'attente.
Sorties et déploiements sans spike
Lorsque je déploie de nouvelles versions, j'évite les caches froids et les tempêtes de processus. J'utilise reload au lieu de redémarrages brutaux, afin que les requêtes de travail existantes se terminent proprement (respecter process_control_timeout). J'échauffe l'OPCache à l'avance en parcourant les chemins critiques avant la commutation ou en travaillant avec le préchargement. J'évite ainsi que de nombreux workers analysent simultanément des fichiers de classe et que les Temps de réponse augmente brusquement.
Dans les stratégies Blue/Green ou Canary, je laisse la charge augmenter progressivement et j'observe les pages d'état. Ce n'est que lorsque la file d'attente, le taux d'erreur et les latences restent stables que j'augmente la part de trafic. Cette procédure contrôlée permet d'éviter les pics de charge pendant le déploiement.
Spécificités des conteneurs et des VM
Dans les conteneurs, la perception Quantité totale de mémoire souvent inférieur à celui annoncé par l'hôte. J'aligne strictement pm.max_children sur la limite de cgroup et je prévois une réserve contre le tueur d'OOM. Les limites de mémoire en PHP (memory_limit) et l'empreinte par processus doivent aller de pair, sinon il suffit d'un seul écart pour que le conteneur se termine.
Si le swap est absent du conteneur, les ruptures difficiles sont plus probables. C'est pourquoi je garde les processus conservateurs, j'active le recyclage, et je surveille les pics de RSS en charge de production. Plusieurs pools allégés sont ici souvent plus robustes qu'un grand pool monolithique.
Dégradation et backpressure contrôlables
Si la Queue plus vite qu'elle ne peut être réduite, je mise sur une dégradation contrôlée : en cas de surcharge, je livre délibérément 503 avec Retry-After pour les points finaux non critiques, je réduis les fonctionnalités coûteuses (par ex. recherche en direct) et je limite les accès parallèles aux hotspots. De cette manière, le système reste accessible pendant que je corrige la cause, au lieu que tous les utilisateurs soient mis en attente.
En bref
J'apporte Mise en file d'attente des requêtes PHP en faisant correspondre intelligemment le nombre de processus simultanés au budget RAM et au type de charge. Des valeurs de backlog élevées amortissent les pics, les délais d'attente à tous les niveaux s'imbriquent proprement et le recyclage élimine les problèmes de mémoire insidieux. Les logs et les métriques me montrent si la file d'attente augmente, où les requêtes sont bloquées et quand je dois les affiner. Grâce à des adaptations prudentes et à une mise en cache ciblée, je réduis le temps de traitement par requête et j'augmente le débit. Ainsi, les serveurs fournissent des données cohérentes et évitent les erreurs coûteuses. Timeouts dans la vie quotidienne.


