Mejorar el rendimiento
Almacenamiento en caché de páginas
1. Alrededor del protocolo HTTP
Symfony se basa en las especificaciones HTTP para la gestión de la caché de las páginas. El protocolo HTTP define una serie de encabezados relacionados con el almacenamiento en caché.
He aquí una muestra rápida de sus funcionalidades:
<?php header('Cache-Control: max-age=10') ?>
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<?php echo date('y-m-d H:i:s') ?><br />
<a href="">Actualizar</a>
</body>
</html>
Si abre esta página con un navegador web y luego hace clic en el botón Actualizar, se dará cuenta de que la fecha mostrada se actualiza solo después de 10 segundos.
Durante otras cargas, el navegador utiliza la página presente en su caché porque, cuando recupera la respuesta por primera vez, la cabecera Cache-Control indica que la página se puede almacenar en caché durante 10 segundos; pasado este tiempo, se debe solicitar una nueva versión al servidor web.
Gracias a esta técnica, la página se puede devolver al usuario muy rápidamente, no se realiza ninguna petición HTTP y se reduce la carga en el servidor web.
2. Un servidor proxy inverso o «reverse proxy»
Desafortunadamente, esta técnica de almacenamiento en caché en el «lado cliente» muestra sus límites con mucha rapidez.
Dado que las páginas se almacenan en caché a nivel del navegador, un nuevo usuario, al abrir la página por primera vez, debe enviar una petición al servidor web para recuperar la página que se almacenará en caché. Su aplicación debe regenerar constantemente la misma página para cada nuevo usuario: la caché no se comparte.
El servidor web debe generar la página para cada cliente y, si se realiza una nueva petición para la misma página y las cabeceras indican que la copia sigue siendo válida (Cache-Control), utilizan la copia...
Carga automática de clases
Sabemos que Symfony se basa en Composer para la carga automática de clases (consulte el capítulo Arquitectura del framework - Carga automática de clases). Cargar atómicamente una clase no es una acción pesada, pero esta acción se repite miles de veces durante una consulta en un proyecto Symfony. Por lo tanto, almacenar en caché el archivo asociado con cada una de las clases utilizadas por su proyecto es una optimización interesante.
Generar un classmap
Con la opción -o del comando dump-autoload, Composer regenera un cargador de clases más potente y con mejor rendimiento: se crea una tabla que contiene cada clase del proyecto, asociada a su ubicación física.
El cargador de clases utiliza esta última para encontrar muy rápidamente el archivo que se debe incluir para cargar una clase determinada.
He aquí el comando en cuestión:
composer dump-autoload -o
Si su proyecto tiene muchas clases o dependencias, la tabla que se va a cargar para cada ejecución de script podría ser engorrosa. Si es muy exigente con los recursos utilizados por su sitio web, es posible que le interese la siguiente técnica.
La caché con Doctrine
1. Los diferentes tipos de caché
Doctrine tiene tres tipos de caché.
La caché de metadatos
Los metadatos son toda la información de mapping de sus entidades. Estos datos no cambiarán una vez que la aplicación se despliegue en producción. Por lo tanto, se pueden almacenar en caché indefinidamente.
La caché de consultas
El proceso de transformación de consultas DQL en consultas SQL se puede almacenar en caché. Mientras no se modifique una consulta DQL, su equivalente en SQL siempre será el mismo. Por lo tanto, se recomienda utilizar la caché de consultas en producción.
La caché de resultados
La caché de resultados le permite guardar el resultado de una consulta, de forma que se evita realizar demasiadas consultas a la base de datos.
Se configura utilizando el método useResultCache() del objeto Query de Doctrine:
$query = $manager->createQuery(
'SELECT u FROM App:Usuario u'
);
$query->useResultCache(true, 60);
El primer argumento indica que la memoria caché de resultados debe estar habilitada y el segundo argumento define la duración (en segundos) de la validez de los resultados almacenados en caché. Si se omite el segundo argumento, los resultados se almacenarán en caché indefinidamente....
La caché de anotaciones
Si utiliza anotaciones para tareas como definir rutas o reglas de validación, seguro que le interesa esta optimización.
Como ya hemos explicado (consulte el capítulo Enrutamiento y controlador - Definir rutas), el soporte de las anotaciones no es nativo del lenguaje PHP, sino que lo proporciona Doctrine y se basa en la API de introspección de PHP. Esta última no está destinada a su uso en producción, sino más bien para herramientas de análisis de código o pruebas.
Por este motivo, de forma predeterminada, Doctrine almacena en caché las anotaciones en los archivos. Sin embargo, este sistema de caché se puede optimizar.
# config/packages/framework.yaml
framework:
annotations:
cache: mi_cache_doctrine
# config/services.yaml
services:
memcached:
class: Memcached
arguments: ['mi_conexion']
calls:
- [addServer, ["localhost", 11211]] ...
Las sesiones
De forma predeterminada, las sesiones se guardan en archivos. Este comportamiento es fácilmente modificable y aquí se explica cómo guardar sesiones en memoria compartida usando Memcache:
# config/packages/framework.yaml
framework:
# ....
session:
handler_id: mi_sesion_memcache
# config/services.yaml
services:
memcached:
class: Memcached
arguments: ['mi_conexion']
calls:
- [addServer, ["localhost", 11211]]
mi_sesion_memcache:
class:
Symfony\Component\HttpFoundation\Session\Storage\Handler\Memcached
SessionHandler
arguments: [@memcached]
Existen otros sistemas de copia de seguridad (llamados handlers). Para obtener una lista completa, consulte la siguiente URL: https://symfony.com/doc/5.4/session.html
Otras optimizaciones
Las optimizaciones presentadas hasta ahora son en su mayoría específicas de Symfony. Sin embargo, un proyecto bajo este framework difícilmente escapa a optimizaciones más genéricas.
Como el resto de este capítulo no trata específicamente sobre Symfony, nuestro enfoque estará orientado a conceptos y teoría, con el objetivo de que sea conciso. Continuaremos sugiriendo herramientas adecuadas a lo largo de nuestras explicaciones.
1. Elegir su SAPI PHP
a. ¿Qué es una SAPI?
Una SAPI (Server Application Programming Interface) define el modo de comunicación entre el sistema y un programa PHP.
PHP tiene varios SAPI, ya que se puede utilizar en una multitud de entornos, cada uno con sus propios aspectos específicos. De esta manera, los programas en línea de comandos usan la SAPI CLI, mientras que los programas relacionados con la web usan la SAPI CGI o FastCGI, por ejemplo.
Dependiendo de la SAPI utilizada, PHP no se invoca de la misma manera y tiene diferencias en la ejecución. Por ejemplo, desde la línea de comandos, puede pasar argumentos a sus scripts y recuperarlos en la variable $argv. En un entorno web, las variables superglobales $_GET y $_POST contienen información sobre la petición HTTP. Aquí, nos vamos a centrar en el entorno web y presentaremos las diferentes formas de integrar PHP con un servidor web.
b. Módulo del servidor
Esta técnica de integración ha sido durante mucho tiempo la más popular. Consiste en «cargar» PHP directamente en los procesos del servidor web. Esto es lo que hace Apache mod_php, por ejemplo.
Si bien este tipo de integración ofrece un buen rendimiento, todavía tiene un gran inconveniente: la gestión de permisos. Dado que sus scripts PHP se ejecutan dentro de Apache, tendrán los mismos permisos que el usuario de Apache. Esta limitación dificulta la gestión de permisos, especialmente en un entorno compartido.
Para evitar esto, algunos desarrolladores eligen la simplicidad al otorgar al usuario de Apache más permisos de los necesarios (esto puede llegar hasta la adición de ese usuario al grupo root) y, desafortunadamente, crean vulnerabilidades de seguridad.
c. CGI
La interfaz CGI (Common Gateway Interface) es un conjunto de especificaciones que definen la manera en que un servidor...
Prueba del rendimiento de un sitio web
Durante la fase de desarrollo, algunas veces es difícil juzgar la efectividad de las optimizaciones que se implementan. Puede suceder que algunas de ellas, una vez desplegadas en producción, no aporten ninguna mejora en el rendimiento, ni siquiera aumenten los tiempos de respuesta de la aplicación.
1. Lado del servidor
a. Apache Bench
Apache Bench es una herramienta que mide la capacidad de su servidor web para el escalado. Permite hacer múltiples peticiones a su servidor web.
Si está utilizando el servidor web Apache, Apache Bench ya debería estar instalado en su máquina.
Si no utiliza Apache y está en Ubuntu/Debian, puede recuperarlo instalando el paquete apache2-utils. Si está en otro sistema operativo, deberá instalar Apache.
Utilización
El siguiente comando hace peticiones a su sitio web durante 30 segundos (opción -t) con un nivel de simultaneidad de 100 (opción -c):
ab -t 30 -c 100 http://www.mi-proyecto.demo/
El nivel de concurrencia indica el número de peticiones que se enviarán al mismo tiempo. Aquí, el servidor procesa 100 peticiones simultáneamente: por cada respuesta devuelta por el servidor, Apache Bench envía una nueva petición, de modo que procesa constantemente 100 peticiones.
b. Xhprof
Xhprof es una extensión PHP creada por Facebook. Proporciona información detallada sobre...