Entonces, entiendo cómo funciona Node.js: tiene un solo hilo de escucha que recibe un evento y luego lo delega a un grupo de trabajadores. El hilo de trabajo notifica al oyente una vez que completa el trabajo, y el oyente luego devuelve la respuesta al llamador.
Mi pregunta es la siguiente: si levanto un servidor HTTP en Node.js y llamo a sleep en uno de los eventos de mi ruta enrutada (como "/ test / sleep"), todo el sistema se detiene. Incluso el hilo de un solo oyente. Pero tengo entendido que este código está sucediendo en el grupo de trabajadores.
Ahora, por el contrario, cuando uso Mongoose para hablar con MongoDB, las lecturas de DB son una operación de E / S costosa. El nodo parece poder delegar el trabajo a un hilo y recibir la devolución de llamada cuando se completa; el tiempo necesario para cargar desde la base de datos no parece bloquear el sistema.
¿Cómo decide Node.js utilizar un hilo de grupo de subprocesos frente al hilo de escucha? ¿Por qué no puedo escribir un código de evento que duerma y solo bloquee un subproceso del grupo de subprocesos?
Respuestas:
Su comprensión de cómo funciona el nodo no es correcta ... pero es un error común, porque la realidad de la situación es bastante compleja y, por lo general, se reduce a pequeñas frases concisas como "el nodo es de un solo subproceso" que simplifican demasiado las cosas .
Por el momento, ignoraremos el multiproceso / multiproceso explícito a través del clúster y los subprocesos de trabajo web , y solo hablaremos del nodo típico sin subprocesos.
El nodo se ejecuta en un solo bucle de eventos. Es de un solo hilo, y solo obtienes ese hilo. Todo el javascript que escribe se ejecuta en este bucle, y si ocurre una operación de bloqueo en ese código, bloqueará todo el bucle y no sucederá nada más hasta que finalice. Esta es la naturaleza típicamente de un solo subproceso del nodo de la que tanto se habla. Pero no es el panorama completo.
Ciertas funciones y módulos, generalmente escritos en C / C ++, admiten E / S asíncronas. Cuando llamas a estas funciones y métodos, administran internamente el paso de la llamada a un hilo de trabajo. Por ejemplo, cuando usa el
fs
módulo para solicitar un archivo, elfs
módulo pasa esa llamada a un hilo de trabajo, y ese trabajador espera su respuesta, que luego presenta al bucle de eventos que se ha estado agitando sin él en el mientras tanto. Todo esto se extrae de usted, el desarrollador de nodos, y parte de esto se extrae de los desarrolladores de módulos mediante el uso de libuv .Como señaló Denis Dollfus en los comentarios (de esta respuesta a una pregunta similar), la estrategia utilizada por libuv para lograr E / S asíncronas no siempre es un grupo de subprocesos, específicamente en el caso del
http
módulo, una estrategia diferente parece ser utilizado en este momento. Para nuestros propósitos aquí, es principalmente importante notar cómo se logra el contexto asincrónico (usando libuv) y que el grupo de subprocesos mantenido por libuv es una de las múltiples estrategias que ofrece esa biblioteca para lograr la asincronicidad.En una tangente principalmente relacionada, hay un análisis mucho más profundo de cómo el nodo logra la asincronicidad, y algunos problemas potenciales relacionados y cómo lidiar con ellos, en este excelente artículo . La mayor parte se expande sobre lo que he escrito anteriormente, pero además señala:
UV_THREADPOOL_SIZE
variable de entorno, siempre que lo haga antes de que se requiera y se cree el grupo de subprocesos:process.env.UV_THREADPOOL_SIZE = 10;
Si desea un multiproceso o subprocesamiento múltiple tradicional en el nodo, puede obtenerlo a través del
cluster
módulo integrado o varios otros módulos como el mencionado anteriormentewebworker-threads
, o puede falsificarlo implementando alguna forma de dividir su trabajo y usar manualmentesetTimeout
osetImmediate
oprocess.nextTick
para pausar su trabajo y continuarlo en un ciclo posterior para permitir que se completen otros procesos (pero eso no es recomendable).Tenga en cuenta que si está escribiendo código de bloqueo / ejecución prolongada en javascript, probablemente esté cometiendo un error. Otros idiomas funcionarán de manera mucho más eficiente.
fuente
Esto no es realmente exacto. Node.js tiene un solo hilo "trabajador" que ejecuta javascript. Hay subprocesos dentro del nodo que manejan el procesamiento de E / S, pero pensar en ellos como "trabajadores" es un error. En realidad, solo hay manejo de IO y algunos otros detalles de la implementación interna del nodo, pero como programador no puede influir en su comportamiento más que unos pocos parámetros misceláneos como MAX_LISTENERS.
No hay ningún mecanismo de suspensión en JavaScript. Podríamos discutir esto de manera más concreta si publicaras un fragmento de código de lo que crees que significa "dormir". No existe tal función para llamar para simular algo como
time.sleep(30)
en Python, por ejemplo. HaysetTimeout
pero que es fundamentalmente no duerme.setTimeout
y liberarsetInterval
explícitamente , no bloquear, el bucle de eventos para que otros bits de código puedan ejecutarse en el hilo de ejecución principal. Lo único que puede hacer es hacer un bucle ocupado en la CPU con el cálculo en memoria, lo que de hecho hará que el subproceso de ejecución principal deje de responder y su programa no responda.La E / S de red siempre es asincrónica. Fin de la historia. Disk IO tiene API síncronas y asincrónicas, por lo que no hay "decisión". node.js se comportará de acuerdo con las funciones principales de la API a las que llama sync vs async normal. Por ejemplo:
fs.readFile
vsfs.readFileSync
. Para los procesos secundarios, también hay separadachild_process.exec
ychild_process.execSync
APIs.La regla general es usar siempre las API asincrónicas. Las razones válidas para usar las API de sincronización son el código de inicialización en un servicio de red antes de que esté escuchando conexiones o en scripts simples que no aceptan solicitudes de red para herramientas de compilación y ese tipo de cosas.
fuente
fs
, hasta donde yo séGrupo de hilos cómo cuándo y quién lo utilizó:
En primer lugar, cuando usamos / instalamos Node en una computadora, inicia un proceso entre otros procesos que se llama proceso de nodo en la computadora, y sigue ejecutándose hasta que lo matas. Y este proceso en ejecución es nuestro llamado hilo único.
Por lo tanto, el mecanismo de un solo hilo facilita el bloqueo de una aplicación de nodo, pero esta es una de las características únicas que Node.js trae a la mesa. Entonces, nuevamente, si ejecuta su aplicación de nodo, se ejecutará en un solo hilo. No importa si tiene 1 o un millón de usuarios accediendo a su aplicación al mismo tiempo.
Entonces, entendamos exactamente qué sucede en el hilo único de nodejs cuando inicia su aplicación de nodo. Al principio, se inicializa el programa, luego se ejecuta todo el código de nivel superior, lo que significa que todos los códigos que no están dentro de ninguna función de devolución de llamada ( recuerde que todos los códigos dentro de todas las funciones de devolución de llamada se ejecutarán en el bucle de eventos ).
Después de eso, todo el código de los módulos ejecutado y luego registra toda la devolución de llamada, finalmente, se inició el ciclo de eventos para su aplicación.
Entonces, como discutimos antes, todas las funciones de devolución de llamada y los códigos dentro de esas funciones se ejecutarán en el bucle de eventos. En el bucle de eventos, las cargas se distribuyen en diferentes fases. De todos modos, no voy a discutir sobre el ciclo de eventos aquí.
Bueno, en aras de una mejor comprensión del grupo de subprocesos, le solicito que imagine que en el bucle de eventos, los códigos dentro de una función de devolución de llamada se ejecutan después de completar la ejecución de códigos dentro de otra función de devolución de llamada, ahora si hay algunas tareas son realmente demasiado pesadas. Luego bloquearían nuestro hilo único de nodejs. Y ahí es donde entra el grupo de subprocesos, que es como el bucle de eventos, que la biblioteca libuv proporciona a Node.js.
Por lo tanto, el grupo de subprocesos no es parte de nodejs en sí mismo, libuv lo proporciona para descargar tareas pesadas a libuv, y libuv ejecutará esos códigos en sus propios subprocesos y, después de la ejecución, libuv devolverá los resultados al evento en el bucle de eventos.
El grupo de subprocesos nos da cuatro subprocesos adicionales, que están completamente separados del subproceso principal único. Y de hecho podemos configurarlo hasta 128 hilos.
Entonces, todos estos hilos juntos formaron un grupo de hilos. y el bucle de eventos puede descargar automáticamente tareas pesadas al grupo de subprocesos.
La parte divertida es que todo esto sucede automáticamente detrás de escena. No somos nosotros los desarrolladores quienes decidimos qué va al grupo de subprocesos y qué no.
Hay muchas tareas que van al grupo de subprocesos, como
fuente
Este malentendido es simplemente la diferencia entre la multitarea preventiva y la multitarea cooperativa ...
El sueño apaga todo el carnaval porque en realidad hay una línea para todos los juegos, y cerraste la puerta. Piense en ello como "un intérprete de JS y algunas otras cosas" e ignore los hilos ... para usted, solo hay un hilo, ...
... así que no lo bloquees.
fuente