¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
  1. Libros
  2. Symfony 5
  3. Mejorar el rendimiento
Extrait - Symfony 5 Desarrolle sitios web PHP estructurados y eficientes
Extractos del libro
Symfony 5 Desarrolle sitios web PHP estructurados y eficientes Volver a la página de compra del libro

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.

images/cap14_pag3.png

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...