Uno de los servidores WEB más populares del mundo, si no el que más, es Apache, aunque hay otros como, NGINX, que también son muy utilizados.
El caso, es que para que una página WEB vaya rápida, no basta sólo con optimizar la base de datos, el código fuente, que el servidor sea rápido, etc. El servidor WEB también tiene que estar optimizado para que la WEB cargue rápidamente y Google la posicione mejor.
Echa un vistazo a los consejos de SEO.
A continuación, te voy a explicar algunos trucos que te van a servir para optimizar el servidor WEB Apache.
Optimizando el consumo de memoria de Apache (MaxClients)
Recientemente, estaba notando un gran consumo de memoria en un servidor, a la vez que había aumentado considerablemente el número de procesos de Apache.
Tras analizarlo, me di cuenta de que las siguientes directivas estaban descompensadas (asumo que estamos trabajando en modo prefork):
- StartServers: Es el número de procesos de apache con los que arranca nuestro servidor Web.
- MaxClients: Es el número máximo de procesos hijo que se crearán para atender todas las peticiones WEB recibidas.
- MaxSpareServers: Número máximo de procesos hijo esperando peticiones (idle). No debe ser demasiado alto para evitar problemas de consumo de recursos.
- MinSpareServers: Número de procesos hijo iniciales van a estar esperando peticiones. No debe ser demasiado bajo para que Apache no tenga que estar creando nuevos procesos hijos continuamente.
- ServerLimit: Su valor siempre será igual a MaxClients.
- MaxRequestPerChild: Número máximo de peticiones que puede atender un proceso hijo. Una vez alcanzado el valor configurado, el proceso morirá y liberará sus recursos.
Si reviso cuánta memoria utiliza cada proceso de Apache, observo que está entre 70 y 90MB aproximadamente:

Así que si tomo 80MB de memoria como media y lo multiplico por 10 procesos de apache corriendo, significa que Apache estará consumiendo 800MB cuando tiene esos 10 procesos en marcha.
Por lo tanto, tendremos que mirar cuánta memoria libre tenemos en nuestro servidor cuando paramos Apache, para conocer el número máximo de procesos de Apache que tenemos que configurar como máximo. Es decir, el valor que pondremos en la directiva MaxClients.
Si hay más usuarios conectados que el número indicado en MaxClients, las nuevas peticiones se encolarán, por lo que MaxClients también significa el número máximo de conexiones simultáneas al servidor.
Yo procuro reservar un 20% de memoria libre para no agotar toda la que tenemos disponible en el servidor.
Ejemplo de configuración del módulo prefork:
<IfModule prefork.c>
StartServers 5
MinSpareServers 10
MaxSpareServers 15
ServerLimit 25
MaxClients 25
MaxRequestsPerChild 4000
</IfModule>
Reiniciamos Apache y comprobamos el número de procesos de Apache que han arrancado y la cantidad de memoria libre que tenemos:

Configura las directivas deflate
Las directivas deflate de Apache habilitan la compresión de los ficheros antes de ser descargados. De esta manera, si nos tenemos que descargar menos datos, más rápidamente se cargará una página WEB (no es lo mismo descargarse 100KB, que 50). Si esto se aplica a cada petición que recibimos en nuestro sitio, nos ahorramos mucho tráfico de descarga.
Obviamente, la compresión de ficheros utiliza CPU en el servidor WEB, pero si no vamos justos de consumo, no supondrá ningún problema. Más bien, se traducirá en una mejora de la velocidad de carga de nuestra WEB.
Para habilitar estas directivas, simplemente hay que tocar nuestro fichero de apache (httpd.conf) y añadir:
LoadModule deflate_module /usr/lib64/httpd/modules/mod_deflate.so
<IfModule mod_deflate.c>
# Especificamos los tipos de archivos que queremos que se compriman
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/atom_xml
AddOutputFilterByType DEFLATE application/rdf+xml
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/x-httpd-php
AddOutputFilterByType DEFLATE application/x-httpd-fastphp
AddOutputFilterByType DEFLATE application/x-httpd-eruby
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-font-ttf application/x-font-otf
AddOutputFilterByType DEFLATE font/truetype font/opentype
# Deshabilitamos la compresiM-sn deflate para cualquier tipo de contenido
#SetOutputFilter DEFLATE. Esta se puede dejar habilitada, si los filtros anteriores fallan.
#SetInputFilter DEFLATE
#AddOutputFilterByType DEFLATE application/x-httpd-php
# Niveles de compresion
DeflateCompressionLevel 7
DeflateMemLevel 8
DeflateWindowSize 10
</IfModule>
Luego, reiniciamos Apache.
Comprobar que la compresión con deflate está funcionando
A continuación, vamos a hacer una pequeña prueba para comprobar que Apache comprime los ficheros:
- Miramos el tamaño del fichero jquery.js
[root@prt53ws1 Apache]# ls -la jquery.js
-rwx------ 1 puerto53 puerto53 97184 May 23 2016 jquery.js
[root@prt53ws1 Apache]#
- Ahora accedemos a él con wget:
[root@prt53ws1 Apache]# wget --header="Accept-Encoding: gzip" https://puerto53.com/wp-includes/js/jquery/jquery.js
--2018-04-15 06:38:23-- https://puerto53.com/wp-includes/js/jquery/jquery.js
Resolving puerto53.com (puerto53.com)... 10.0.1.5
Connecting to puerto53.com (puerto53.com)|10.0.1.5|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/javascript]
Saving to: ‘jquery.js’
[ <=> ] 42,859 --.-K/s in 0s
2018-04-15 06:38:23 (209 MB/s) - ‘jquery.js’ saved [42859]
[root@prt53ws1 Apache]#
Como vemos, la compresión ha sido de la mitad del tamaño del fichero, aproximadamente.
Unificar las directivas deflate con políticas para minimizar html, mejorará el rendimiento de nuestra WEB, un factor clave para el SEO.
Habilita la caché del navegador desde el propio Apache
La primera vez que un usuario accede a nuestra página WEB, se descarga todo el contenido necesario para su carga. Sin embargo, con las directivas de Apache Expires y Cache-Control, habilitaremos ciertos ficheros para guardar en la propia caché del navegador del usuario para que no tenga que descargarse, una y otra vez los mismos ficheros cada vez que accede a nuestro sitio.
Yo lo tengo configurado así:
<ifModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 5 seconds"
ExpiresByType image/x-icon "access plus 2592000 seconds"
ExpiresByType image/jpeg "access plus 2592000 seconds"
ExpiresByType image/png "access plus 2592000 seconds"
ExpiresByType image/gif "access plus 2592000 seconds"
ExpiresByType application/x-shockwave-flash "access plus 2592000 seconds"
ExpiresByType text/css "access plus 604800 seconds"
ExpiresByType text/javascript "access plus 216000 seconds"
ExpiresByType application/javascript "access plus 216000 seconds"
ExpiresByType application/x-javascript "access plus 216000 seconds"
ExpiresByType text/html "access plus 600 seconds"
ExpiresByType application/xhtml+xml "access plus 600 seconds"
</ifModule>
<ifModule mod_headers.c>
<filesMatch "\.(ico|jpe?g|png|gif|swf)$">
Header set Cache-Control "public"
</filesMatch>
<filesMatch "\.(css)$">
Header set Cache-Control "public"
</filesMatch>
<filesMatch "\.(js)$">
Header set Cache-Control "private"
</filesMatch>
<filesMatch "\.(x?html?|php)$">
Header set Cache-Control "private, must-revalidate"
</filesMatch>
</ifModule>
Si la WEB está escrita con PHP, utiliza PHP-FM
PHP-FPM (Fast CGI Process Manager) es un producto que permite integrarse con Apache 2 para atender diferentes peticiones PHP de manera simultánea para mejorar el rendimiento y la carga de la página WEB, lo que se traduce en menores tiempos de carga y muchas más peticiones concurrentes en el mismo servidor WEB.
Una de las características de PHP-FPM que mejora la velocidad de carga es la reutilización del código PHP almacenado en memoria caché. Es decir, en vez de interpretar el código PHP cada vez, lo interpreta la primera vez y el resto de las veces lo reutiliza, evitando nuevos cálculos de CPU.
Lo he probado en puerto53.com y he notado una mejora notable en la carga de este blog.
La instalación es muy sencilla.
En Linux CentOs 7, instalamos PHP-FPM así:
yum install -y php72w-fpm
systemctl enable php-fpm
Seguidamente, editamos fichero /etc/php-fpm.d/www.conf y modificamos las siguientes directivas:
;dominio de la URL
[puerto53.com]
;usuario de sistema operativo que arranca Apache
user = puerto53
;grupo al que pertenece el usuario
group = puerto53
;La IP y el puerto por donde va a escuchar el servicio de PHP-FPM
listen = 127.0.0.1:9000
;El usuario del sistema operativo propietario del servicio
listen.owner = puerto53
Ejemplo de configuración de este fichero:
[root@prt53ws1 ~]# cat /etc/php-fpm.d/www.conf |grep -v ^$ |grep -v ";"
[puerto53.com]
user = puerto53
group = puerto53
listen = 127.0.0.1:9000
listen.owner = puerto53
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 12
pm.start_servers = 8
pm.min_spare_servers = 4
pm.max_spare_servers = 10
slowlog = /var/log/php-fpm/www-slow.log
php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
php_value[session.save_handler] = files
php_value[session.save_path] = /var/lib/php/session
php_value[soap.wsdl_cache_dir] = /var/lib/php/wsdlcache
[root@prt53ws1 ~]#
Arrancamos el servicio de PHP-FPM con el comando systemctl start php-fpm y comprobamos que el puerto 9000 ya está escuchando al estar el servicio arrancado:
[root@prt53ws1 ~]# lsof -i:9000
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
php-fpm 3162 root 6u IPv4 361759 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 3163 puerto53 8u IPv4 361759 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 3164 puerto53 8u IPv4 361759 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 3165 puerto53 8u IPv4 361759 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 3166 puerto53 8u IPv4 361759 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 3167 puerto53 3u IPv4 435157 0t0 TCP localhost:cslistener->localhost:52054 (ESTABLISHED)
php-fpm 3167 puerto53 8u IPv4 361759 0t0 TCP localhost:cslistener (LISTEN)
httpd 7494 puerto53 15u IPv4 436554 0t0 TCP localhost:52054->localhost:cslistener (ESTABLISHED)
php-fpm 7514 puerto53 8u IPv4 361759 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 7528 puerto53 8u IPv4 361759 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 7550 puerto53 8u IPv4 361759 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 7634 puerto53 8u IPv4 361759 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 7638 puerto53 8u IPv4 361759 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 21240 puerto53 8u IPv4 361759 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 21246 puerto53 8u IPv4 361759 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 21270 puerto53 8u IPv4 361759 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 21275 puerto53 8u IPv4 361759 0t0 TCP localhost:cslistener (LISTEN)
[root@prt53ws1 ~]#
Por último, configuramos Apache. Simplemente hay que añadir las siguientes directiva:
# php-fpm
SetEnv proxy-nokeepalive 1
<IfModule proxy_module>
ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/app/puerto53/html/$1 timeout=600
ProxyTimeout 600
</IfModule>
# php-fpm Fin
El módulo de Apache proxy_module, debe estar habilitado.
Reiniciamos Apache y ya tenemos PHP-FPM funcionando.
Cálculo correcto del parámetro pm.max_children de PHP-FPM para un rendimiento óptimo
Lo primero que tenemos que saber es cuánta memoria consume nuestra aplicación. Lo averiguaremos con el comando:
ps -ylC php-fpm --sort:rss

En este caso vemos en la columna RSS que cada proceso de aplicación ocupa unos 90MB, aproximadamente.
A continuación, revisamos cuánta memoria libre tenemos en nuestra maquina si paramos PHP-FPM. Lo veremos con el comando:
free -m

En este caso tenemos 1GB totalmente libre y unos 550MB de buffer caché que se pueden considerar como libres.
Pero no queremos agotar toda la memoria del servidor, así que decidimos que, como máximo, el servicio PHP-FPM utilice 1100MB de memoria. Por lo tanto:
pm.max_children: 1100 / 92 = 11,9
El parámetro pm.max_children lo configuraremos con un valor de 12.
No olvides realizar pruebas de estrés con el comando ab (Apache Bench)
Antes de publicar una página WEB al gran público, es conveniente hacer pruebas de estrés de los servidores WEB, revisar el rendimiento del Garbage Collector de Java (si la aplicación está hecha en Java) y de la base de datos, para saber si los usuarios van a experimentar problemas de lentitud o no cuando pongamos el servicio en producción.
Si es una WEB pública en Internet, hay que recordar que Google penaliza el posicionamiento cuando la página carga con lentitud. Hay estadísticas que dicen que si una página tarda más de tres segundos en cargar, se pierde el 40% de los usuarios.
Pruebas de carga con ab simulando 100 peticiones y 10 usuarios concurrentes
En el caso que nos ocupa, ab o Apache Bench nos sirve para realizar pruebas de estrés sobre una URL. Por ejemplo, lanzar 100 peticiones simulando 10 usuarios concurrentes.
Ejemplo:
[root@prt53ws1 ~]# ab -n 100 -c 10 www.google.com/index.html
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking www.google.com (be patient).....done
Server Software: gws
Server Hostname: www.google.com
Server Port: 80
Document Path: /index.html
Document Length: 10848 bytes
Concurrency Level: 10
Time taken for tests: 3.826 seconds
Complete requests: 100
Failed requests: 98
(Connect: 0, Receive: 0, Length: 98, Exceptions: 0)
Write errors: 0
Total transferred: 1152610 bytes
HTML transferred: 1086110 bytes
Requests per second: 26.14 [#/sec] (mean)
Time per request: 382.625 [ms] (mean)
Time per request: 38.262 [ms] (mean, across all concurrent requests)
Transfer rate: 294.18 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 138 139 1.0 139 142
Processing: 203 209 4.6 208 242
Waiting: 165 171 4.5 170 204
Total: 340 348 5.0 347 383 --> Petición más lenta 383ms y más rápida 340ms
Percentage of the requests served within a certain time (ms)
50% 347
66% 349
75% 349
80% 350
90% 354
95% 355
98% 357
99% 383
100% 383 (longest request)
[root@prt53ws1 ~]#
Te puede interesar
- Configuración de un servidor Memcached
- Instalar WordPress con NGINX y PHP-FM
- Configurar el plugin LiteSpeed Cache para cachear WordPress
- Tutorial de WordPress
- Los Mejores Servidores VPS para un buen rendimiento de tu WEB