Node tiene un paradigma completamente diferente y una vez que se captura correctamente, es más fácil ver esta forma diferente de resolver problemas. Nunca necesita múltiples subprocesos en una aplicación Node (1) porque tiene una forma diferente de hacer lo mismo. Creas múltiples procesos; pero es muy diferente a, por ejemplo, cómo funciona el mpm Prefork de Apache Web Server.
Por ahora, pensemos que solo tenemos un núcleo de CPU y desarrollaremos una aplicación (a la manera de Node) para hacer algo de trabajo. Nuestro trabajo consiste en procesar un archivo grande que se ejecuta en su contenido byte a byte. La mejor manera para nuestro software es comenzar el trabajo desde el principio del archivo, seguirlo byte a byte hasta el final.
- ¡¡Oye, Hasan, supongo que o eres un novato o eres muy viejo de la época de mi abuelo !!! ¿Por qué no creas algunos hilos y lo haces mucho más rápido?
- Oh, solo tenemos un núcleo de CPU.
-- ¿Y qué? Crea algunos hilos, ¡hazlo más rápido!
-- No funciona así. Si creo hilos, lo haré más lento. Porque agregaré mucha sobrecarga al sistema para cambiar entre subprocesos, tratando de darles una cantidad justa de tiempo, y dentro de mi proceso, tratando de comunicarse entre estos subprocesos. Además de todos estos hechos, también tendré que pensar en cómo dividiré un solo trabajo en múltiples partes que se pueden hacer en paralelo.
- Está bien, está bien, veo que eres pobre. Usemos mi computadora, ¡tiene 32 núcleos!
- Vaya, eres increíble mi querido amigo, muchas gracias. ¡Lo aprecio!
Luego volvemos al trabajo. Ahora tenemos 32 núcleos de CPU gracias a nuestro rico amigo. Las reglas que tenemos que cumplir acaban de cambiar. Ahora queremos utilizar toda esta riqueza que se nos da.
Para usar múltiples núcleos, necesitamos encontrar una manera de dividir nuestro trabajo en partes que podamos manejar en paralelo. Si no fuera Node, usaríamos subprocesos para esto; 32 subprocesos, uno para cada núcleo de CPU. Sin embargo, dado que tenemos Node, crearemos procesos de 32 Node.
Los subprocesos pueden ser una buena alternativa a los procesos de Node, tal vez incluso una mejor manera; pero solo en un tipo específico de trabajo donde el trabajo ya está definido y tenemos un control completo sobre cómo manejarlo. Aparte de esto, para cualquier otro tipo de problema en el que el trabajo proviene de afuera de una manera sobre la que no tenemos control y queremos responder lo más rápido posible, la forma de Node es indiscutiblemente superior.
- Oye, Hasan, ¿sigues trabajando con un solo hilo? ¿Qué te pasa, hombre? Te acabo de proporcionar lo que querías. Ya no tienes excusas. Crea hilos, haz que corra más rápido.
- He dividido el trabajo en piezas y cada proceso trabajará en una de estas piezas en paralelo.
- ¿Por qué no creas hilos?
- Lo siento, no creo que sea utilizable. ¿Puedes llevarte tu computadora si quieres?
- No, está bien, soy genial, simplemente no entiendo por qué no usas hilos.
- Gracias por la computadora. :) Ya dividí el trabajo en piezas y creo procesos para trabajar en estas piezas en paralelo. Todos los núcleos de la CPU se utilizarán por completo. Podría hacer esto con subprocesos en lugar de procesos; pero Node tiene esta manera y mi jefe Parth Thakkar quiere que use Node.
- De acuerdo, avíseme si necesita otra computadora. :pags
Si creo 33 procesos, en lugar de 32, el programador del sistema operativo pausará un hilo, iniciará el otro, lo pausará después de algunos ciclos, iniciará el otro de nuevo ... Esto es una sobrecarga innecesaria. No lo quiero. De hecho, en un sistema con 32 núcleos, ni siquiera quisiera crear exactamente 32 procesos, 31 pueden ser mejores . Porque no es solo mi aplicación la que funcionará en este sistema. Dejar un poco de espacio para otras cosas puede ser bueno, sobre todo si tenemos 32 habitaciones.
Creo que ahora estamos en la misma página acerca de la utilización completa de procesadores para tareas intensivas en CPU .
- Hmm, Hasan, lamento burlarme un poco de ti. Creo que ahora te entiendo mejor. Pero todavía hay algo para lo que necesito una explicación: ¿Qué es todo el rumor acerca de ejecutar cientos de subprocesos? Leí en todas partes que los hilos son mucho más rápidos de crear y tontos que los procesos de bifurcación. Usted bifurca procesos en lugar de subprocesos y cree que es lo más alto que obtendría con Node. Entonces, ¿Node no es apropiado para este tipo de trabajo?
- No te preocupes, yo también soy genial. Todo el mundo dice estas cosas, así que creo que estoy acostumbrado a escucharlas.
-- ¿Entonces? ¿El nodo no es bueno para esto?
- Node es perfectamente bueno para esto, aunque los subprocesos también pueden ser buenos. En cuanto a la sobrecarga de creación de subprocesos / procesos; en las cosas que repites mucho, cada milisegundo cuenta. Sin embargo, creo solo 32 procesos y tomará una pequeña cantidad de tiempo. Sucederá solo una vez. No hará ninguna diferencia.
- ¿Cuándo quiero crear miles de hilos, entonces?
- Nunca querrás crear miles de hilos. Sin embargo, en un sistema que está realizando un trabajo que proviene del exterior, como un servidor web que procesa solicitudes HTTP; si está utilizando un hilo para cada solicitud, creará muchos hilos, muchos de ellos.
- ¿Pero el nodo es diferente? ¿Correcto?
-- Sí exactamente. Aquí es donde Node realmente brilla. Como un hilo es mucho más ligero que un proceso, una llamada a función es mucho más ligera que un hilo. El nodo llama a funciones, en lugar de crear subprocesos. En el ejemplo de un servidor web, cada solicitud entrante provoca una llamada de función.
-- Mmmm interesante; pero solo puede ejecutar una función al mismo tiempo si no está utilizando varios subprocesos. ¿Cómo puede funcionar esto cuando llegan muchas solicitudes al servidor web al mismo tiempo?
- Tiene toda la razón sobre cómo se ejecutan las funciones, una a la vez, nunca dos en paralelo. Quiero decir, en un solo proceso, solo se ejecuta un alcance de código a la vez. El Programador del SO no viene y pausa esta función y cambia a otra, a menos que pause el proceso para dar tiempo a otro proceso, no a otro hilo de nuestro proceso. (2)
- Entonces, ¿cómo puede un proceso manejar 2 solicitudes a la vez?
- Un proceso puede manejar decenas de miles de solicitudes a la vez siempre que nuestro sistema tenga suficientes recursos (RAM, red, etc.). La forma en que se ejecutan esas funciones es LA DIFERENCIA CLAVE.
- Hmm, ¿debería estar emocionado ahora?
- Quizás :) El nodo ejecuta un bucle sobre una cola. En esta cola están nuestros trabajos, es decir, las llamadas que comenzamos a procesar las solicitudes entrantes. El punto más importante aquí es la forma en que diseñamos nuestras funciones para que se ejecuten. En lugar de comenzar a procesar una solicitud y hacer que la persona que llama espere hasta que terminemos el trabajo, finalizamos rápidamente nuestra función después de realizar una cantidad aceptable de trabajo. Cuando llegamos a un punto en el que necesitamos esperar a que otro componente haga algún trabajo y nos devuelva un valor, en lugar de esperar eso, simplemente terminamos nuestra función agregando el resto del trabajo a la cola.
- ¿Suena demasiado complejo?
- No, no, puedo sonar complejo; pero el sistema en sí es muy simple y tiene perfecto sentido.
Ahora quiero dejar de citar el diálogo entre estos dos desarrolladores y terminar mi respuesta después de un último ejemplo rápido de cómo funcionan estas funciones.
De esta manera, estamos haciendo lo que normalmente haría OS Scheduler. Pausamos nuestro trabajo en algún momento y dejamos que se ejecuten otras llamadas a funciones (como otros subprocesos en un entorno de subprocesos múltiples) hasta que tengamos nuestro turno nuevamente. Esto es mucho mejor que dejar el trabajo a OS Scheduler, que intenta dar el tiempo justo a cada hilo del sistema. Sabemos lo que estamos haciendo mucho mejor que OS Scheduler y se espera que nos detengamos cuando deberíamos detenernos.
A continuación se muestra un ejemplo simple en el que abrimos un archivo y lo leemos para trabajar un poco con los datos.
Manera sincrónica:
Open File
Repeat This:
Read Some
Do the work
Manera asincrónica:
Open File and Do this when it is ready: // Our function returns
Repeat this:
Read Some and when it is ready: // Returns again
Do some work
Como ves, nuestra función pide al sistema que abra un archivo y no espera a que se abra. Termina por sí solo proporcionando los siguientes pasos después de que el archivo esté listo. Cuando regresamos, Node ejecuta otras llamadas a funciones en la cola. Después de ejecutar todas las funciones, el bucle de eventos pasa al siguiente turno ...
En resumen, Node tiene un paradigma completamente diferente al del desarrollo multiproceso; pero esto no quiere decir que le falten cosas. Para un trabajo sincrónico (donde podemos decidir el orden y la forma de procesamiento), funciona tan bien como el paralelismo multiproceso. Para un trabajo que proviene del exterior, como solicitudes a un servidor, simplemente es superior.
(1) A menos que esté creando bibliotecas en otros lenguajes como C / C ++, en cuyo caso aún no crea subprocesos para dividir trabajos. Para este tipo de trabajo, tiene dos hilos, uno de los cuales continuará la comunicación con Node mientras que el otro hace el trabajo real.
(2) De hecho, cada proceso de nodo tiene varios subprocesos por las mismas razones que mencioné en la primera nota al pie. Sin embargo, esto no es como 1000 hilos haciendo trabajos similares. Esos subprocesos adicionales son para cosas como aceptar eventos de E / S y manejar mensajes entre procesos.
ACTUALIZAR (Como respuesta a una buena pregunta en los comentarios)
@Mark, gracias por la crítica constructiva. En el paradigma de Node, nunca debe tener funciones que tarden demasiado en procesarse, a menos que todas las demás llamadas en la cola estén diseñadas para ejecutarse una tras otra. En el caso de tareas computacionalmente costosas, si miramos la imagen en su totalidad, vemos que esto no es una cuestión de "¿Deberíamos usar hilos o procesos?" pero una pregunta de "¿Cómo podemos dividir estas tareas de una manera bien equilibrada en subtareas para poder ejecutarlas en paralelo empleando múltiples núcleos de CPU en el sistema?" Digamos que procesaremos 400 archivos de video en un sistema con 8 núcleos. Si queremos procesar un archivo a la vez, entonces necesitamos un sistema que procese diferentes partes del mismo archivo, en cuyo caso, tal vez, un sistema multiproceso de un solo proceso será más fácil de construir e incluso más eficiente. Todavía podemos usar Node para esto ejecutando múltiples procesos y pasando mensajes entre ellos cuando sea necesario compartir el estado / comunicación. Como dije antes, un enfoque multiproceso con Node esasí como un enfoque multiproceso en este tipo de tareas; pero no más que eso. Nuevamente, como dije antes, la situación en la que Node brilla es cuando tenemos estas tareas como entrada al sistema desde múltiples fuentes, ya que mantener muchas conexiones al mismo tiempo es mucho más liviano en Node en comparación con un hilo por conexión o un proceso por conexión. sistema.
En cuanto a setTimeout(...,0)
llamadas; a veces, puede ser necesario dar un descanso durante una tarea que requiere mucho tiempo para permitir que las llamadas en la cola tengan su parte de procesamiento. Dividir las tareas de diferentes maneras puede evitarlo; pero aún así, esto no es realmente un truco, es solo la forma en que funcionan las colas de eventos. Además, usar process.nextTick
para este fin es mucho mejor ya que cuando lo uses setTimeout
, será necesario calcular y verificar el tiempo transcurrido mientras que process.nextTick
es simplemente lo que realmente queremos: "Oye tarea, vuelve al final de la cola, ¡has usado tu parte! "