Arrancar aplicaciones en Node.js es algo muy sencillo, simplemente ejecutamos el comando node fichero.js
y listo, ya tenemos nuestra aplicación funcionando. Pero... ¿qué ocurre si nuestra aplicación se cae o sufre un error? Pues que no volverá a funcionar hasta que reiniciemos el proceso. Esta tarea se puede llevar a cabo automáticamente mediante proyectos como forever o node-supervisor, que mantienen el proceso siempre funcionando.

Por otro lado, el funcionamiento estándar de Node.js se basa en un único hilo de ejecución, por lo que un solo proceso es el encargado de gestionar todo. Gracias al procesamiento asíncrono, Node.js es capaz de gestionar las peticiones muy rápidamente pero si contamos con más CPUs, se estarán desaprovechando recursos y esto es lo que vamos a mejorar a continuación.
Para aprovechar los núcleos podemos utilizar un magnífico gestor de procesos llamado PM2 creado por Unitech. Desgraciadamente, tras usarlo durante unas semanas lo considero inestable y no apto para el uso en producción, por lo que utilizaremos forever
(npm install -g forever
) y el módulo clúster de Node.js.
Utilizando el módulo clúster
El módulo clúster de Node.js se encuentra en desarrollo (Stability: 1 - Experimental
), pero nos ha parecido lo suficientemente estable como para usarlo en producción. El único problema que hemos notado es que las peticiones no se distribuyen tan aleatoriamente a través de los procesos como nos gustaría, pero recordad que al ser experimental, ¡bajo vuestra cuenta y riesgo!
En el siguiente ejemplo lo que haremos será crear un fichero llamado server.js
que lo ejecutaremos mediante forever. Este fichero será el maestro y se encargará de crear esclavos que iniciarán nuestra aplicación, cuyo punto de entrada es app.js
.
Para darle uso al módulo clúster, simplemente tenemos que importar el módulo. Si queremos autodetectar el número de procesadores, importamos también os
.
A continuación asignamos a la variable workers
el número de procesos que vamos a correr simultáneamente. Podemos hacerlo automáticamente mediante:
const workers = oslength
También configuramos el clúster para que los procesos ejecuten nuestra aplicación:
cluster
Para facilitar el registro, hemos creado una función:
{
console
}
Iniciamos los esclavos mediante cluster.fork()
:
for let i = 0; i < workers; i++
cluster
Después utilizamos dos eventos para registrar cuando nacen y mueren los procesos. Además, si mueren serán reiniciados automáticamente:
cluster
cluster
Por último, para ejecutar el proceso maestro de una manera que nos garantice la máxima disponibilidad posible, ejecutamos el proceso mediante el comando forever server.js
.
Algo a tener en cuenta utilizando el módulo clúster es que al tratarse de procesos independientes, lo que haya en memoria no se compartirá con el resto de procesos esclavos. Por lo tanto, debemos utilizar almacenamiento de datos compartido tipo mongodb o redis para compartir datos entre procesos.
Pruebas
Aunque las siguientes pruebas no están científicamente demostradas ni ningún dentista/dermatólogo las ha aprobado, si servirán para hacernos una idea. Hemos utilizado boom porque es compatible con ARM (siege no lo es) y funciona bien. El comando para pruebas es el siguiente (50000 peticiones, 100 concurrentes):
x86_64 - Intel Xeon E5-2680 v2 @ 2.80GHz (2 procesos)
Modo normal:
Total: 7.9854 secs. Slowest: 0.1416 secs. Fastest: 0.0019 secs. Average: 0.0159 secs. Requests/sec: 6261.3879
Modo clúster:
Total: 5.8005 secs. Slowest: 0.1121 secs. Fastest: 0.0003 secs. Average: 0.0116 secs. Requests/sec: 8619.9772
armv7l - Samsung Exynos4412 Prime Cortex-A9 Quad Core 1.7Ghz (4 procesos)
Modo normal:
Total: 18.7339 secs. Slowest: 0.2366 secs. Fastest: 0.0073 secs. Average: 0.0374 secs. Requests/sec: 2668.9630
Modo clúster:
Total: 14.0123 secs. Slowest: 0.2929 secs. Fastest: 0.0010 secs. Average: 0.0280 secs. Requests/sec: 3568.2954
Como podemos ver el incremento de peticiones por segundo es del 35%. Si tenemos en cuenta que nuestra aplicación no será un simple hello world, esta diferencia puede ser importante.
TL;DR
A continuación, el fichero server.js
entero para que podáis copiar y pegar fácilmente:
const workers = oslength
cluster
{
console
}
for let i = 0; i < workers; i++
cluster
cluster
cluster
También está disponible el ejemplo en GitHub a través del repositorio felixsanz/nodejs-cluster-module.