PHP está en todas partes y es posiblemente el lenguaje más ampliamente desplegado en la Web de Internet
Sin embargo, no es precisamente conocido por sus capacidades de alto rendimiento, especialmente cuando se trata de sistemas altamente concurrentes. Y esa es la razón por la que, para casos de uso tan especializados, se utilizan lenguajes como Node (sí, lo sé, no es un lenguaje), Vaya a y Elixir están tomando el relevo
Dicho esto, hay MUCHO que puede hacer para mejorar el PHP performance en su servidor. Este artículo se centra en el lado php-fpm
que es la forma natural de configurar en su servidor si está utilizando Nginx
En caso de que sepa lo que es php-fpm
, siéntase libre de saltar a la sección sobre optimización
¿Qué es PHP-fpm?
No muchos desarrolladores están interesados en el lado DevOps de las cosas, e incluso entre los que lo hacen, muy pocos saben lo que está pasando bajo el capó. Curiosamente, cuando el navegador envía una solicitud a un servidor que ejecuta PHP, no es PHP el que constituye el punto de primer contacto, sino el servidor HTTP, los principales de los cuales son Apache y Nginx. Estos "servidores web" tienen entonces que decidir cómo conectarse con PHP, y pasarle el tipo de petición, los datos y las cabeceras

En las aplicaciones PHP modernas, la parte de "encontrar archivo" que aparece más arriba es el index.
php, en el que el servidor está configurado para delegar todas las peticiones
Ahora bien, la forma exacta en que el servidor web se conecta a PHP ha evolucionado, y este artículo explotaría en longitud si tuviéramos que entrar en todos los detalles. Pero a grandes rasgos, durante el tiempo en que Apache dominó como el servidor web de elección, PHP era un módulo incluido dentro del servidor
Así, cada vez que se recibía una petición, el servidor iniciaba un nuevo proceso, que incluía automáticamente PHP, y lo hacía ejecutar. Este método se llamaba mod_php
, abreviatura de "PHP como módulo" Este enfoque tenía sus limitaciones, que Nginx superó con php-fpm
En php-fpm
la responsabilidad de gestionar PHP, los procesos recaen en el programa PHP dentro del servidor. En otras palabras, al servidor web (Nginx, en nuestro caso), no le importa dónde está PHP y cómo se carga, siempre y cuando sepa cómo enviar y recibir datos de él. Si quiere, puede pensar en PHP en este caso como otro servidor en sí mismo, que gestiona algunos procesos PHP hijos para las peticiones entrantes (así, tenemos la petición llegando a un servidor, que fue recibida por un servidor y pasada a otro servidor - ¡bastante loco! :-P)
Si ha realizado alguna configuración de Nginx, o incluso simplemente ha curioseado en ellas, se encontrará con algo como esto
ubicación ~ .php$ { try_files $uri =404; fastcgi_split_path_info ^(. .php)(/. )$; fastcgi_pass unix:/run/php/php7.2-fpm.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; }
La línea que nos interesa es esta: fastcgi_pass unix:/run/php/php7.2-fpm.sock;
, que indica a Nginx que se comunique con el proceso PHP a través del socket llamado php7.2-fpm.sock
. Así, por cada petición entrante, Nginx escribe datos a través de este archivo, y al recibir la salida, la envía de vuelta al navegador
Una vez más, debo enfatizar que esta no es la imagen más completa o más precisa de lo que sucede, pero es totalmente exacta para la mayoría de las tareas DevOps
Dejando esto a un lado, recapitulemos lo que hemos aprendido hasta ahora
- PHP no recibe directamente las peticiones enviadas por los navegadores. Los servidores web como Nginx primero las interceptan.
- El servidor web sabe cómo conectarse al proceso PHP, y pasa todos los datos de la petición (literalmente lo pega todo) a PHP.
- Cuando PHP termina de hacer su parte, devuelve la respuesta al servidor web, que la envía de nuevo al cliente (o navegador, en la mayoría de los casos).
O gráficamente

Genial hasta aquí, pero ahora viene la pregunta del millón: ¿qué es exactamente PHP-FPM?
La parte "FPM" en PHP significa "Gestor Rápido de Procesos", que no es más que una forma elegante de decir que el PHP que se ejecuta en un servidor no es un único proceso, sino más bien algunos procesos PHP que son generados, controlados y eliminados por este gestor de procesos FPM. Es a este gestor de procesos al que el servidor web pasa las peticiones
El PHP-FPM es toda una madriguera de conejo en sí misma, así que siéntase libre de explorarla si lo desea, pero para nuestros propósitos, esta explicación será suficiente 🙂 ...
¿Por qué optimizar PHP-fpm?
¿Por qué preocuparse por todo este baile cuando las cosas funcionan bien? ¿Por qué no dejar las cosas como están?
Irónicamente, ese es precisamente el consejo que doy para la mayoría de los casos de uso. Si su configuración funciona bien y no tiene casos de uso extraordinarios, utilice los valores predeterminados. Sin embargo, si está buscando escalar más allá de una sola máquina, entonces exprimir el máximo de una es esencial ya que puede reducir las facturas del servidor a la mitad (¡o incluso más!)
Otra cosa a tener en cuenta es que Nginx fue construido para manejar enormes cargas de trabajo. Es capaz de manejar miles de conexiones al mismo tiempo, pero si no ocurre lo mismo con su configuración PHP, sólo va a malgastar recursos ya que Nginx tendrá que esperar a que PHP termine con el proceso actual y acepte el siguiente, ¡negando definitivamente cualquier ventaja para la que Nginx fue construido!
Así que, con eso fuera del camino, veamos qué es exactamente lo que cambiaríamos al tratar de optimizar php-fpm
¿Cómo optimizar PHP-FPM?
La ubicación del archivo de configuración para php-fpm
puede diferir en el servidor, por lo que necesitará investigar un poco para localizarlo. Puede utilizar el comando find si está en UNIX. En mi Ubuntu, la ruta es /etc/php/7.2/fpm/php-fpm.conf
. La 7.2 es, por supuesto, la versión de PHP que estoy ejecutando
Aquí está el aspecto de las primeras líneas de este archivo
;;;;;;;;;;;;;;;;;;;;; ; Configuración FPM ; ;;;;;;;;;;;;;;;;;;;;; ; Todas las rutas relativas en este archivo de configuración son relativas al prefijo de instalación de PHP ; (/usr). Este prefijo puede ser cambiado dinámicamente usando el argumento ; '-p' desde la línea de comandos. ;;;;;;;;;;;;;;;;;; ; Opciones Globales ; ;;;;;;;;;;;;;;;;;; [globales] ; Archivo Pid ; Nota: el prefijo por defecto es /var ; Valor por defecto: ninguno pid = /run/php/php7.2-fpm.pid ; Archivo de registro de errores ; Si se establece como "syslog", el registro se envía a syslogd en lugar de escribirse ; en un archivo local. ; Nota: el prefijo por defecto es /var ; Valor por defecto: log/php-fpm.log error_log = /var/log/php7.2-fpm.log
Algunas cosas deberían ser inmediatamente obvias: la línea pid = /run/php/php7.2-fpm.pid
nos dice qué archivo contiene el id de proceso del proceso php-fpm
También vemos que /var/log/php7.2-fpm.
log es donde php-fpm
va a almacenar sus registros
Dentro de este archivo, añada tres variables más como estas
umbral_reinicio_emergencia 10 intervalo de reinicio de emergencia 1m process_control_timeout 10s
Los dos primeros ajustes son de precaución y le están diciendo al proceso php-fpm
que si diez procesos hijos fallan en un minuto, el proceso principal php-fpm
debe reiniciarse a sí mismo
Esto puede no sonar robusto, pero PHP es un proceso de corta duración que sí tiene fugas de memoria, por lo que reiniciar el proceso principal en casos de alto fallo puede resolver muchos problemas.
La tercera opción, process_control_timeout
, indica a los procesos hijos que esperen este tiempo antes de ejecutar la señal recibida del proceso padre. Esto es útil en los casos en los que los procesos hijos están en medio de algo cuando los procesos padres envían una señal KILL, por ejemplo. Con diez segundos a mano, tendrán más posibilidades de terminar sus tareas y salir con elegancia
Sorprendentemente, ¡este no es el meollo de la configuración de php-fpm
¡! Eso es porque para servir peticiones web, el php-fpm
crea un nuevo pool de procesos, que tendrá una configuración separada. En mi caso, el nombre del pool resultó ser www
y el archivo que quería editar era /etc/php/7.2/fpm/pool.d/www.conf
Veamos cómo empieza este archivo
; Inicie un nuevo pool llamado 'www'. ; la variable $pool puede usarse en cualquier directiva y será reemplazada por el ; nombre del pool ('www' aquí) [www] ; Por prefijo del pool ; Sólo se aplica en las siguientes directivas: ; - 'access.log' ; - 'slowlog' (registro lento) ; - 'listen' (unixsocket) ; - 'chroot' ; - 'chdir' ; - 'php_values' ; - 'php_admin_values' ; Cuando no se establece, se aplica en su lugar el prefijo global (o /usr). ; Nota: Esta directiva también puede ser relativa al prefijo global. ; Valor por defecto: ninguno ;prefijo = /ruta/a/pools/$pool ; Usuario Unix/grupo de procesos ; Nota: El usuario es obligatorio. Si no se establece el grupo, se utilizará el grupo del usuario por defecto ; usuario = www-datos grupo = www-data
Un rápido vistazo al final del fragmento anterior resuelve el enigma de por qué el proceso del servidor se ejecuta como www-data
. Si se ha encontrado con problemas de permisos de archivos al configurar su sitio web, es probable que haya cambiado el propietario o el grupo del directorio a www-data
, permitiendo así que el proceso PHP pueda escribir en archivos de registro y cargar documentos, etc
Finalmente, llegamos al origen del asunto, la configuración del gestor de procesos (pm). Generalmente, verá que los valores por defecto son algo como esto
pm = dinámico pm.max_hijos = 5 pm.servidores_inicio = 3 pm.min_servidores_reserva = 2 pm.max_servidores_reserva = 4 pm.max_peticiones = 200
Entonces, ¿qué significa"dinámico"¿Aquí? Creo que los documentos oficiales lo explican mejor (es decir, esto ya debería formar parte del archivo que está editando, pero lo he reproducido aquí por si acaso no es así)
; Elija cómo controlará el gestor de procesos el número de procesos hijos. ; Valores posibles: ; estático - un número fijo (pm.max_children) de procesos hijos; ; dinámico - el número de procesos hijos se establece dinámicamente basándose en las ; siguientes directivas. Con esta gestión de procesos, siempre habrá ; al menos 1 hijo. ; pm.max_children - el número máximo de hijos que pueden ; estar vivos al mismo tiempo. ; pm.start_servers - el número de hijos creados al iniciarse. ; pm.min_spare_servers - el número mínimo de hijos en estado 'inactivo' ; (esperando para procesar). Si el número ; de procesos 'idle' es menor que este número ; entonces se crearán algunos hijos. ; pm.max_spare_servers - el número máximo de hijos en estado 'idle' ; (esperando para procesar). Si el número ; de procesos 'idle' es mayor que este ; número entonces algunos hijos serán eliminados. ; ondemand - no se crean hijos al inicio. Los hijos se bifurcarán cuando ; se conecten nuevas peticiones.
Se
utilizan los siguientes parámetros: ; pm.max_children - el número máximo de hijos que ; pueden estar vivos al mismo tiempo. ; pm.process_idle_timeout - El número de segundos tras los cuales ; un proceso inactivo será matado. ; Nota: Este valor es obligatorio
Por lo tanto, vemos que hay tres valores posibles
- Estático: Se mantendrá un número fijo de procesos PHP pase lo que pase.
- Dinámico: Podemos especificar el número mínimo y máximo de procesos que
php-fpm
mantendrá vivos en un momento dado. - bajo demanda: Los procesos son creados y destruidos, bueno, bajo demanda.
Entonces, ¿qué importancia tienen estos ajustes?
En términos sencillos, si tiene un sitio web con poco tráfico, el ajuste "dinámico" es un desperdicio de recursos la mayor parte del tiempo. Suponiendo que tenga pm.min_spare_servers
establecido en 3, se crearán y mantendrán tres procesos PHP incluso cuando no haya tráfico en el sitio web. En tales casos, "bajo demanda" es una mejor opción, dejando que el sistema decida cuándo lanzar nuevos procesos
Por otro lado, los sitios web que manejan grandes cantidades de tráfico o que deban responder rápidamente se verán castigados en esta configuración. Crear un nuevo proceso PHP, hacerlo parte de un pool, y monitorizarlo, es una sobrecarga extra que es mejor evitar
Utilice pm =
static fija el número de procesos hijos, dejando que se utilicen el máximo de recursos del sistema en servir las peticiones en lugar de gestionar PHP. Si opta por esta vía, tenga en cuenta que tiene sus pautas y trampas. Un artículo bastante denso pero muy útil sobre ello está aquí
Palabras finales
Dado que los artículos sobre el rendimiento web pueden desatar guerras o servir para confundir a la gente, creo que son necesarias unas palabras antes de cerrar este artículo. El ajuste del rendimiento tiene tanto que ver con conjeturas y artes oscuras como con el conocimiento del sistema
Incluso si conoce de memoria todos los ajustes de php-fpm
el éxito no está garantizado. Si no tiene ni idea de la existencia de php-fpm
Entonces no necesita perder el tiempo preocupándose por ello. Simplemente siga haciendo lo que ya está haciendo y continúe
Al mismo tiempo, evita acabar siendo un adicto al rendimiento. Sí, puede obtener un rendimiento aún mejor recompilando PHP desde cero y eliminando todos los módulos que no vaya a utilizar, pero este enfoque no es lo suficientemente sensato en entornos de producción. La idea de optimizar algo es echar un vistazo a si sus necesidades difieren de las predeterminadas (¡que rara vez lo hacen!), y hacer pequeños cambios según sea necesario.
Si no está preparado para dedicar tiempo a la optimización de sus servidores PHP, entonces puede considerar la posibilidad de recurrir a una plataforma fiable como Kinsta, que se encarga de la optimización del rendimiento y la seguridad.
-
Escribo sobre, alrededor y para el ecosistema de los desarrolladores. Recomendaciones, tutoriales, debates técnicos... Sea lo que sea lo que publique, hago todo lo posible por eliminar la confusión y la palabrería y ofrecer respuestas prácticas basadas en la experiencia personal... Seguir leyendo