Node.js en máquinas multinúcleo
Node.js parece interesante, PERO debo perderme algo: ¿no está Node.js configurado para ejecutarse solo en un único proceso y subproceso?
Entonces, ¿cómo se escala para CPU de múltiples núcleos y servidores de múltiples CPU? Después de todo, es genial hacer un servidor de un solo subproceso lo más rápido posible, pero para cargas elevadas me gustaría usar varias CPU. Y lo mismo ocurre con hacer que las aplicaciones sean más rápidas: hoy parece que la forma es utilizar múltiples CPU y paralelizar las tareas.
¿Cómo encaja Node.js en esta imagen? ¿Su idea es distribuir de alguna manera varias instancias o qué?
[ Esta publicación está actualizada a partir del 2 de septiembre de 2012 (más reciente que la anterior). ]
Node.js escala absolutamente en máquinas multinúcleo.
Sí, Node.js tiene 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 código multiproceso. Para obtener una explicación más profunda del modelo de proceso de Node.js y por qué funciona de esta manera (y por qué NUNCA admitirá múltiples subprocesos), lea mi otra publicación .
Entonces, ¿cómo aprovecho mi caja de 16 núcleos?
Dos caminos:
- Para tareas informáticas grandes y pesadas, como la codificación de imágenes, Node.js puede activar procesos secundarios o enviar mensajes a procesos de trabajo adicionales. En este diseño, tendría un subproceso que administraría el flujo de eventos y N procesos realizando tareas informáticas pesadas y consumiendo las otras 15 CPU.
- Para escalar el rendimiento de un servicio web, debe ejecutar varios servidores Node.js en una caja, uno por núcleo y dividir el tráfico de solicitudes entre ellos. Esto proporciona una excelente afinidad de CPU y escalará el rendimiento casi linealmente con el número de núcleos.
Escalar el rendimiento en un servicio web
Desde la versión 6.0.X, Node.js ha incluido el módulo de clúster listo para usar, lo que facilita la configuración de varios nodos trabajadores que pueden escuchar en un solo puerto. Tenga en cuenta que esto NO es lo mismo que el módulo de "clúster" de learnboost anterior 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 tendrá más probabilidades de ganar. Funciona bastante bien y puede aumentar bastante bien el rendimiento en una caja de múltiples núcleos.
Si tiene suficiente carga para preocuparse por varios núcleos, entonces también querrá hacer algunas cosas más:
Ejecute su servicio Node.js detrás de un proxy web como Nginx o Apache , algo que pueda acelerar la conexión (a menos que desee que las condiciones de sobrecarga derriben el cuadro por completo), reescribir URL, ofrecer contenido estático y representar otros subservicios.
Recicle periódicamente sus procesos de trabajo. Para un proceso de larga duración, incluso una pequeña pérdida de memoria eventualmente se sumará.
Configurar la recopilación/monitoreo 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 un Apache prebifurcado haciendo esto con la importante advertencia de que cada proceso solo aceptará una única conexión y luego morirá. La pérdida de eficiencia para Apache se debe a 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 una interfaz integrada como Nginx y enviar ese tráfico proxy a 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 desees tener Nginx (o una alternativa) al frente de tu 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 la configuración de puertos individuales tiene algunos beneficios (potencial de tener menos 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 integrado es de bajo costo. -Alternativa de complejidad que funciona para la mayoría de las personas.
Un método sería ejecutar varias instancias de node.js en el servidor y luego colocar un equilibrador de carga (preferiblemente uno sin bloqueo como nginx) delante de ellas.
Ryan Dahl responde a 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 que les permita comunicarse. Por ejemplo, IPC estilo sendmsg() o RPC tradicional".
Si quieres ensuciarte las manos de inmediato, consulta el módulo spark2 Forever . Hace que generar procesos de múltiples nodos sea trivialmente fácil. Maneja la configuración del puerto compartido, para que cada uno pueda aceptar conexiones al mismo puerto, y también la reaparición automática 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. Forever también merece una mirada.