Node.js en máquinas multinúcleo

606

Node.js parece interesante, PERO debo perder algo, ¿No está Node.js sintonizado solo para ejecutarse en un solo proceso y subproceso?

Entonces, ¿cómo se escala para CPU multi-core y servidores multi-CPU? Después de todo, es genial hacer un servidor de un solo hilo lo más rápido posible, pero para cargas elevadas me gustaría usar varias CPU. Y lo mismo se aplica a hacer que las aplicaciones sean más rápidas: parece que hoy en día la forma es usar múltiples CPU y paralelizar las tareas.

¿Cómo encaja Node.js en esta imagen? ¿Es su idea distribuir múltiples instancias o qué?

zaharpopov
fuente
44
Parece que Ryah está empezando a tomarse en serio la inclusión del soporte multinúcleo incorporado en el nodo: github.com/joyent/node/commit/…
broofa
2
El administrador de procesos PM2 utiliza el módulo de clúster internamente para difundir sus aplicaciones NodeJS a todos los núcleos disponibles: github.com/Unitech/pm2
Unitech
@broofa, esos no son hilos reales y los procesos secundarios no tienen memoria compartida. Consulte también ¿Cuál es el equivalente de Nodejs de las variables de subprocesamiento real y volátil-estática de Java? .
Pacerier

Respuestas:

697

[ Esta publicación está actualizada a partir del 2012-09-02 (más reciente que la anterior). ]

Node.js escala absolutamente en máquinas de múltiples núcleos.

Sí, Node.js es un subproceso por proceso. Esta es una decisión de diseño muy deliberada y elimina la necesidad de lidiar con la semántica de bloqueo. Si no está de acuerdo con esto, probablemente aún no se dé cuenta de lo increíblemente difícil que es depurar el código de subprocesos múltiples. Para una explicación más profunda del modelo de proceso Node.js y por qué funciona de esta manera (y por qué NUNCA admitirá múltiples hilos), lea mi otra publicación .

Entonces, ¿cómo aprovecho mi caja de 16 núcleos?

Dos caminos:

  • Para grandes tareas de cómputo pesado como la codificación de imágenes, Node.js puede iniciar procesos secundarios o enviar mensajes a procesos de trabajo adicionales. En este diseño, tendría un subproceso que administra el flujo de eventos y N procesos que realizan tareas de computación pesadas y mastican las otras 15 CPU.
  • Para escalar el rendimiento en un servicio web, debe ejecutar varios servidores Node.js en un cuadro, uno por núcleo y dividir el tráfico de solicitudes entre ellos. Esto proporciona una excelente afinidad con la CPU y escalará el rendimiento casi linealmente con el recuento de núcleos.

Escalado de rendimiento en un servicio web

Desde v6.0.X Node.js ha incluido el módulo de clúster directamente, lo que facilita la configuración de múltiples trabajadores de nodo que pueden escuchar en un solo puerto. Tenga en cuenta que esto NO es lo mismo que el antiguo módulo "cluster" de learnboost disponible a través de npm .

if (cluster.isMaster) {
  // Fork workers.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
} else {
  http.Server(function(req, res) { ... }).listen(8000);
}

Los trabajadores competirán para aceptar nuevas conexiones, y el proceso menos cargado es más probable que gane. Funciona bastante bien y puede aumentar el rendimiento bastante bien en una caja de múltiples núcleos.

Si tiene suficiente carga para preocuparse por múltiples núcleos, entonces también querrá hacer algunas cosas más:

  1. Ejecute su servicio Node.js detrás de un proxy web como Nginx o Apache , algo que puede limitar la conexión (a menos que desee que las condiciones de sobrecarga reduzcan completamente el cuadro), reescriba URL, sirva contenido estático y delegue otros subservicios.

  2. Recicla periódicamente tus procesos de trabajo. Para un proceso de larga duración, incluso una pequeña pérdida de memoria eventualmente se acumulará.

  3. Configurar la recopilación / supervisión de registros


PD: Hay una discusión entre Aaron y Christopher en los comentarios de otra publicación (al momento de escribir este artículo, es la publicación principal). Algunos comentarios al respecto:

  • Un modelo de socket compartido es muy conveniente para permitir que múltiples procesos escuchen en un solo puerto y compitan para aceptar nuevas conexiones. Conceptualmente, se podría pensar en Apache preformado haciendo esto con la advertencia importante de que cada proceso solo aceptará una única conexión y luego morirá. La pérdida de eficiencia para Apache está en la sobrecarga de bifurcar nuevos procesos y no tiene nada que ver con las operaciones de socket.
  • Para Node.js, hacer que N trabajadores compitan en un solo socket es una solución extremadamente razonable. La alternativa es configurar un front-end en caja como Nginx y tener ese tráfico proxy para los trabajadores individuales, alternando entre trabajadores para asignar nuevas conexiones. Las dos soluciones tienen características de rendimiento muy similares. Y dado que, como mencioné anteriormente, es probable que desee tener Nginx (o una alternativa) al frente de su servicio de nodo de todos modos, la elección aquí es realmente entre:

Puertos compartidos: nginx (port 80) --> Node_workers x N (sharing port 3000 w/ Cluster)

vs

Puertos individuales: nginx (port 80) --> {Node_worker (port 3000), Node_worker (port 3001), Node_worker (port 3002), Node_worker (port 3003) ...}

Podría decirse que hay algunos beneficios para la configuración de puertos individuales (potencial para tener un menor acoplamiento entre procesos, tener decisiones de equilibrio de carga más sofisticadas, etc.), pero definitivamente es más trabajo configurarlo y el módulo de clúster incorporado es bajo alternativa de complejidad que funciona para la mayoría de las personas.

Dave Dopson
fuente
1
¿Puedes ofrecer algún consejo para ejecutar diferentes servicios basados ​​en nodejs en una caja? Por ejemplo, supongamos que tengo 1 servidor y quiero ejecutar myservice1.js en CpuCore1 y myservice2.js en CpuCore2. ¿Puedo usar cluster para esto? ¿O solo es útil para crear servicios clonados?
UpTheCreek
66
¡Deberías publicar una pregunta para eso! (y copiaré este comentario como tu primera respuesta). Lo que quieres hacer es realmente muy, muy simple. Realmente no necesitarías "clúster", solo ejecutarías dos servicios de nodo diferentes. Dos scripts, dos procesos, dos puertos. Por ejemplo, podría tener el servicio A escuchar en 3000 y el servicio B escuchar en 3001. Cada uno de esos servicios podría usar "cluster" para tener 1+ trabajadores y reciclarlos periódicamente, etc. Luego podría configurar Nginx para escuchar en el puerto 80 y reenviar a El servicio correcto basado en el encabezado entrante "Host" y / o la ruta URL.
Dave Dopson
1
Gracias. Ya publiqué una pregunta relacionada : usted describió más o menos lo que tenía en mente, pero no estoy seguro de cómo apuntar a los núcleos de la CPU (cuando se usa algo como siempre).
UpTheCreek
Gran respuesta ddopson. ¿Cuál es la mejor manera de que dos procesos de nodo se comuniquen entre sí en la misma máquina? ¿Existe un protocolo más rápido que TCP cuando están en la misma máquina?
winduptoy
1
@Serob_b: bueno, sí. Ejecutar una aplicación Node.js en varias máquinas es muy común. No se necesita biblioteca para hacerlo. Simplemente ejecute su código en varias máquinas y distribuya la carga entre ellas. Diseñar su software para que se escale (es decir, almacena el estado en algún tipo de servicio de datos externo en lugar de mantener el estado en la memoria): ese es su trabajo.
Dave Dopson el
45

Un método sería ejecutar varias instancias de node.js en el servidor y luego colocar un equilibrador de carga (preferiblemente uno que no bloquee como nginx) delante de ellos.

Chandra Sekar
fuente
36
node.js es casi tan rápido como nginx, podría poner un equilibrador de carga node.js frente a sus servidores node.js si así lo desea :)
mikeal
26
Ryan específicamente dijo que no hiciera esto hasta que el nodo fuera más estable. La mejor manera es ejecutar nginx delante del nodo.
resopollution
2
En cuanto a nginx delante del nodo, no resolverá ciertos problemas, como si tiene una cola en memoria. Las instancias de 2 nodos no podrán acceder a la cola del otro.
resopollution
55
Además, nginx no es totalmente compatible con HTTP 1.1, por lo que cosas como WebSockets no se pueden representar.
ashchristopher
2
@mikeal, resopollution: estoy muy del lado de Nginx. Choqué varias veces con Node.js varias veces (sin stacktrace, solo muere). Nunca he estrellado Nginx. Nginx listo para usar está configurado con todo tipo de aceleradores razonables. Node.js por defecto continuará aceptando nuevas conexiones en lugar de servir las existentes hasta que el cuadro se caiga ... sí, el cuadro completo; Choqué el kernel en una caja CentOS5 al probar el nodo Node (ahora ESO realmente no se supone que suceda). Llegué un poco y veo un futuro brillante para Node, que podría incluir roles dedicados de tipo LB. Solo que aún no.
Dave Dopson el
30

Ryan Dahl responde esta pregunta en la charla tecnológica que dio en Google el verano pasado. Parafraseando, "simplemente ejecute procesos de múltiples nodos y use algo sensato para permitirles comunicarse. Por ejemplo, sendmsg () - estilo IPC o RPC tradicional".

Si quiere ensuciarse las manos de inmediato, consulte el módulo spark2 Forever . Hace que generar múltiples procesos de nodo sea trivialmente fácil. Se encarga de configurar el uso compartido de puertos, para que cada uno pueda aceptar conexiones al mismo puerto, y también reaparecer automáticamente si desea asegurarse de que un proceso se reinicie si / cuando muere.

ACTUALIZACIÓN - 11/10/11 : El consenso en la comunidad de nodos parece ser que Cluster es ahora el módulo preferido para administrar múltiples instancias de nodos por máquina. Siempre vale la pena echarle un vistazo.

broofa
fuente
8
Forever y Cluster hacen cosas muy diferentes. Incluso podrías usar ambos. Forever reinicia un proceso cuando muere. Cluster gestiona múltiples trabajadores.
Usaría
44
también, el módulo learnboost es suplantado en gran medida por la versión de Cluster integrada en el Nodo v0.6.x (advertencia: la superficie de la API difiere)
Dave Dopson
@broofa ¿Cómo se compara el IPC predeterminado con, digamos, usar Redis o Memcache solo enviando cadenas / datos / matrices entre procesos? ¿Qué camino sería más rápido?
NiCk Newman
1
@broofa, IPC tiene enormes gastos generales en comparación con la memoria compartida real que Java y C son capaces de hacer.
Pacerier
@Pacerier Es cierto, pero la memoria compartida solo resuelve el problema de cómo escalar en el contexto de un único host, sin abordar los problemas de macro necesarios para escalar en muchos hosts. Es decir, cómo correr en la nube.
broofa
20

Puede usar el módulo de clúster . Mira esto .

var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    // Fork workers.
    for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
    });
} else {
    // Workers can share any TCP connection
    // In this case its a HTTP server
    http.createServer(function(req, res) {
        res.writeHead(200);
        res.end("hello world\n");
    }).listen(8000);
}
Sergey Zhukov
fuente
13

Multi-node aprovecha todos los núcleos que pueda tener.
Echa un vistazo a http://github.com/kriszyp/multi-node .

Para necesidades más simples, puede iniciar varias copias de nodo en diferentes números de puerto y colocar un equilibrador de carga frente a ellos.

CyberFonic
fuente
12

Node Js es compatible con la agrupación para aprovechar al máximo las ventajas de su CPU. Si no lo está ejecutando con el clúster, probablemente esté desperdiciando sus capacidades de hardware.

La agrupación en clúster en Node.js le permite crear procesos separados que pueden compartir el mismo puerto del servidor. Por ejemplo, si ejecutamos un servidor HTTP en el puerto 3000, es un servidor que se ejecuta en un solo hilo en un solo núcleo del procesador.

El código que se muestra a continuación le permite agrupar su aplicación. Este código es el código oficial representado por Node.js.

var cluster = require('cluster');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    // Fork workers.
    for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    Object.keys(cluster.workers).forEach(function(id) {
        console.log("I am running with ID : " + cluster.workers[id].process.pid);
    });

    cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
    });
} else {

    //Do further processing.
}

mira este artículo para ver el tutorial completo

Toumi
fuente
11

Como se mencionó anteriormente, Cluster escalará y equilibrará la carga de su aplicación en todos los núcleos.

agregando algo como

cluster.on('exit', function () {
  cluster.fork();
});

Reiniciará cualquier trabajador que falle.

En estos días, muchas personas también prefieren PM2 , que maneja el agrupamiento por usted y también proporciona algunas funciones de monitoreo interesantes .

Luego, agregue Nginx o HAProxy frente a varias máquinas que se ejecutan con clústeres y tendrá múltiples niveles de conmutación por error y una capacidad de carga mucho mayor.

Will Stern
fuente
3
PM2 es ideal para uso en producción. Las herramientas de monitoreo me han ayudado a resolver problemas de memoria con aplicaciones.
mbokil
7

La versión futura del nodo le permitirá bifurcar un proceso y pasarle mensajes, y Ryan ha declarado que quiere encontrar alguna forma de compartir también los controladores de archivos, por lo que no será una implementación directa de Web Worker.

En este momento no hay una solución fácil para esto, pero aún es muy temprano y el nodo es uno de los proyectos de código abierto de más rápido movimiento que he visto, así que espere algo increíble en el futuro cercano.

mikeal
fuente
7

Spark2 se basa en Spark que ahora ya no se mantiene. Cluster es su sucesor, y tiene algunas características interesantes, como generar un proceso de trabajo por núcleo de CPU y volver a generar trabajadores muertos.

El desarrollador
fuente
La pregunta original y muchas de estas respuestas tienen unos meses y el nodo se mueve tan rápido que agradezco que hayas agregado la publicidad sobre Cluster. Después de mirar Cluster y sus ejemplos, se ve exactamente como lo que yo (¿o el OP?) Quiero para Node, ¡gracias!
Riyad Kalla
5

Estoy usando Node Worker para ejecutar procesos de una manera simple desde mi proceso principal. Parece estar funcionando muy bien mientras esperamos que llegue la forma oficial.

christkv
fuente
1
por eso example.js trabajador nodo no pueden correr, mi nodo es 0.3.3 versión pre
Guilin桂林
5

El nuevo chico en el bloque aquí es "Up" de LearnBoost .

Proporciona "recargas de tiempo de inactividad cero" y, además, crea varios trabajadores (de forma predeterminada, el número de CPU, pero es configurable) para proporcionar lo mejor de todos los mundos.

Es nuevo, pero parece ser bastante estable, y lo estoy usando felizmente en uno de mis proyectos actuales.

Roy
fuente
5

El módulo de clúster le permite utilizar todos los núcleos de su máquina. De hecho, puede aprovechar esto en solo 2 comandos y sin tocar su código utilizando un administrador de procesos muy popular pm2 .

npm i -g pm2
pm2 start app.js -i max
Alister
fuente
4

Puede ejecutar su aplicación node.js en múltiples núcleos utilizando el módulo de clúster en combinación con os módulo que se puede utilizar para detectar cuántas CPU tiene.

Por ejemplo, imaginemos que tiene un servermódulo que ejecuta un servidor http simple en el backend y desea ejecutarlo para varias CPU:

// Dependencies.
const server = require('./lib/server'); // This is our custom server module.
const cluster = require('cluster');
const os = require('os');

 // If we're on the master thread start the forks.
if (cluster.isMaster) {
  // Fork the process.
  for (let i = 0; i < os.cpus().length; i++) {
    cluster.fork();
  }
} else {
  // If we're not on the master thread start the server.
  server.init();
}

Oleksii Trekhleb
fuente
0

También es posible diseñar el servicio web como varios servidores independientes que escuchan sockets unix, para que pueda insertar funciones como el procesamiento de datos en procesos separados.

Esto es similar a la mayoría de las arquitecturas de servidor web de desplazamiento / base de datos donde un proceso cgi maneja la lógica empresarial y luego empuja y extrae los datos a través de un socket Unix a una base de datos.

La diferencia es que el procesamiento de datos se escribe como un servidor web de nodo que escucha en un puerto.

es más complejo pero, en última instancia, es donde debe ir el desarrollo multinúcleo. Una arquitectura multiproceso que utiliza múltiples componentes para cada solicitud web.

Cuervo de fuego
fuente
0

Es posible escalar NodeJS a varios cuadros usando un equilibrador de carga TCP puro (HAProxy) frente a varios cuadros que ejecutan un proceso NodeJS cada uno.

Si luego tiene algún conocimiento común para compartir entre todas las instancias, podría usar una tienda Redis central o similar a la que luego se pueda acceder desde todas las instancias de proceso (por ejemplo, desde todos los cuadros)

Martin Tajur
fuente
A menos que tenga CPU de un solo núcleo en esos servidores, eso no va a utilizar toda la capacidad de su CPU (a menos que también esté haciendo otra cosa).
UpTheCreek