Node.js se basa en libuv , una biblioteca multiplataforma que abstrae apis / syscalls para entradas / salidas asíncronas (sin bloqueo) proporcionadas por los sistemas operativos compatibles (Unix, OS X y Windows al menos).
IO asincrónica
En este modelo de programación, la operación de abrir / leer / escribir en dispositivos y recursos (sockets, sistema de archivos, etc.) administrados por el sistema de archivos no bloquea el hilo de llamada (como en el modelo típico síncrono tipo c) y simplemente marca el proceso (en la estructura de datos a nivel de kernel / OS) para ser notificado cuando haya nuevos datos o eventos disponibles. En el caso de una aplicación similar a un servidor web, el proceso es responsable de determinar a qué solicitud / contexto pertenece el evento notificado y procesar la solicitud desde allí. Tenga en cuenta que esto necesariamente significará que estará en un marco de pila diferente del que originó la solicitud al sistema operativo, ya que este último tuvo que ceder al despachador de un proceso para que un solo proceso de subprocesos maneje nuevos eventos.
El problema con el modelo que describí es que no es familiar y difícil de razonar para el programador, ya que no es de naturaleza secuencial. "Debe realizar una solicitud en la función A y manejar el resultado en una función diferente donde los locales de A generalmente no están disponibles".
Modelo de nodo (estilo de paso de continuación y bucle de evento)
Node aborda el problema aprovechando las características del lenguaje javascript para hacer que este modelo tenga un aspecto un poco más sincrónico al inducir al programador a emplear un cierto estilo de programación. Cada función que solicita IO tiene una firma similar function (... parameters ..., callback)
y necesita recibir una devolución de llamada que se invocará cuando se complete la operación solicitada (tenga en cuenta que la mayor parte del tiempo se pasa esperando que el sistema operativo indique la finalización, tiempo que puede ser gastado haciendo otro trabajo). El soporte de Javascript para cierres le permite usar variables que ha definido en la función externa (llamada) dentro del cuerpo de la devolución de llamada; esto permite mantener el estado entre las diferentes funciones que invocará el tiempo de ejecución del nodo de forma independiente. Vea también Continuation Passing Style .
Además, después de invocar una función que genera una operación IO, la función de llamada generalmente return
controlará el bucle de eventos del nodo . Este bucle invocará la siguiente devolución de llamada o función que se programó para su ejecución (muy probablemente porque el SO notificó el evento correspondiente); esto permite el procesamiento concurrente de múltiples solicitudes.
Puede pensar en el bucle de eventos del nodo como algo similar al despachador del núcleo: el núcleo programaría la ejecución de un subproceso bloqueado una vez que se complete su E / S pendiente, mientras que el nodo programará una devolución de llamada cuando se haya producido el evento correspondiente.
Altamente concurrente, sin paralelismo
Como comentario final, la frase "todo se ejecuta en paralelo excepto su código" hace un trabajo decente al capturar el punto en que el nodo permite que su código maneje solicitudes de cientos de miles de sockets abiertos con un solo hilo simultáneamente multiplexando y secuenciando todos sus js lógica en un solo flujo de ejecución (aunque decir que "todo funciona en paralelo" probablemente no sea correcto aquí - ver Concurrencia vs Paralelismo - ¿Cuál es la diferencia? ). Esto funciona bastante bien para los servidores de aplicaciones web, ya que la mayor parte del tiempo se dedica a esperar a la red o al disco (base de datos / sockets) y la lógica no es realmente intensiva en la CPU, es decir: funciona bien para las cargas de trabajo vinculadas a IO .
Bueno, para dar una perspectiva, déjame comparar node.js con apache.
Apache es un servidor HTTP multiproceso, para cada solicitud que recibe el servidor, crea un subproceso independiente que maneja esa solicitud.
Node.js, por otro lado, está controlado por eventos, manejando todas las solicitudes de forma asíncrona desde un solo hilo.
Cuando A y B se reciben en Apache, se crean dos hilos que manejan las solicitudes. Cada uno maneja la consulta por separado, cada uno espera los resultados de la consulta antes de servir la página. La página solo se sirve hasta que finaliza la consulta. La búsqueda de consultas está bloqueando porque el servidor no puede ejecutar el resto del hilo hasta que reciba el resultado.
En el nodo, c.query se maneja de forma asíncrona, lo que significa que mientras c.query obtiene los resultados para A, salta para manejar c.query para B, y cuando los resultados llegan para A llegan, devuelve los resultados a la devolución de llamada que envía el respuesta. Node.js sabe ejecutar la devolución de llamada cuando finaliza la búsqueda.
En realidad, el servidor de nodo hace exactamente eso por usted todo el tiempo. Para hacer cambios, (el comportamiento asincrónico) la mayoría de las funciones que usaría tendrán devoluciones de llamada.
Editar
La consulta SQL se toma de la biblioteca mysql . Implementa el estilo de devolución de llamada, así como el emisor de eventos para poner en cola las solicitudes SQL. No los ejecuta de forma asíncrona, eso es hecho por los hilos internos de libuv que proporcionan la abstracción de E / S sin bloqueo. Los siguientes pasos ocurren para realizar una consulta:
Las solicitudes entrantes al servidor http se manejan de manera similar. La arquitectura interna del hilo es algo como esto:
Los subprocesos de C ++ son los libuv que realizan la E / S asíncrona (disco o red). El bucle principal de eventos continúa ejecutándose después de enviar la solicitud al grupo de subprocesos. Puede aceptar más solicitudes, ya que no espera ni duerme. Las consultas SQL / solicitudes HTTP / lecturas del sistema de archivos suceden de esta manera.
fuente
Node.js usa libuv detrás de escena. libuv tiene un grupo de subprocesos (de tamaño 4 por defecto). Por lo tanto, Node.js usa hilos para lograr concurrencia.
Sin embargo , su código se ejecuta en un solo subproceso (es decir, todas las devoluciones de llamada de las funciones de Node.js se invocarán en el mismo subproceso, el llamado bucle de subproceso o bucle de evento). Cuando la gente dice "Node.js se ejecuta en un solo hilo", en realidad están diciendo "las devoluciones de llamada de Node.js se ejecutan en un solo hilo".
fuente
Node.js se basa en el modelo de programación de bucle de eventos. El bucle de eventos se ejecuta en un solo hilo y espera repetidamente eventos y luego ejecuta cualquier controlador de eventos suscrito a esos eventos. Los eventos pueden ser por ejemplo
Todo esto se ejecuta en un solo hilo y ningún código JavaScript se ejecuta en paralelo. Mientras estos controladores de eventos sean pequeños y esperen aún más eventos, todo funcionará bien. Esto permite que múltiples solicitudes sean manejadas simultáneamente por un solo proceso Node.js.
(Hay un poco de magia debajo del capó como donde se originan los eventos. Algunos de ellos involucran hilos de trabajo de bajo nivel que se ejecutan en paralelo).
En este caso de SQL, suceden muchas cosas (eventos) entre hacer la consulta de la base de datos y obtener sus resultados en la devolución de llamada . Durante ese tiempo, el bucle de eventos continúa bombeando vida a la aplicación y avanzando otras solicitudes, un pequeño evento a la vez. Por lo tanto, se atienden varias solicitudes simultáneamente.
De acuerdo con: "Bucle de eventos de 10,000 pies - concepto central detrás de Node.js" .
fuente
La función c.query () tiene dos argumentos
La operación "Obtener datos" en este caso es una consulta DB, ahora esto puede ser manejado por Node.js generando un subproceso de trabajo y dándole la tarea de realizar la consulta DB. (Recuerde que Node.js puede crear hilos internamente). Esto permite que la función regrese instantáneamente sin demora
El segundo argumento "Postprocesamiento de datos" es una función de devolución de llamada, el marco de nodo registra esta devolución de llamada y es llamado por el bucle de eventos.
Por lo tanto, la declaración
c.query (paramenter1, parameter2)
volverá instantáneamente, permitiendo que el nodo atienda otra solicitud.PD: Acabo de empezar a entender el nodo, en realidad quería escribir esto como comentario a @Philip, pero como no tenía suficientes puntos de reputación, lo escribí como respuesta.
fuente
si lee un poco más: "Por supuesto, en el back-end, hay hilos y procesos para el acceso a la base de datos y la ejecución del proceso. Sin embargo, estos no están expuestos explícitamente a su código, por lo que no puede preocuparse por ellos más que por saber que las interacciones de E / S, por ejemplo, con la base de datos, o con otros procesos, serán asíncronas desde la perspectiva de cada solicitud, ya que los resultados de esos hilos se devuelven a su código a través del bucle de eventos ".
about - "todo se ejecuta en paralelo excepto su código" - su código se ejecuta sincrónicamente, cada vez que invoca una operación asincrónica como esperar IO, el bucle de eventos maneja todo e invoca la devolución de llamada. simplemente no es algo en lo que tengas que pensar.
en su ejemplo: hay dos solicitudes A (viene primero) y B. ejecuta la solicitud A, su código continúa ejecutándose sincrónicamente y ejecuta la solicitud B. el bucle de eventos maneja la solicitud A, cuando finaliza invoca la devolución de llamada de la solicitud A con el resultado, lo mismo pasa con la solicitud B.
fuente
De acuerdo, la mayoría de las cosas deberían estar claras hasta ahora ... la parte difícil es el SQL : si en realidad no se está ejecutando en otro hilo o proceso en su totalidad, la ejecución de SQL debe dividirse en pasos individuales (por un ¡Procesador SQL hecho para ejecución asincrónica!), Donde se ejecutan los que no bloquean, y los que bloquean (por ejemplo, la suspensión) en realidad se pueden transferir al núcleo (como una interrupción / evento de alarma) y poner en la lista de eventos para el bucle principal.
Eso significa que, por ejemplo, la interpretación del SQL, etc. se realiza de inmediato, pero durante la espera (almacenada como un evento que vendrá en el futuro por el núcleo en alguna estructura kqueue, epoll, ...; junto con las otras operaciones IO ) el bucle principal puede hacer otras cosas y eventualmente verificar si algo sucedió con esos IO y espera.
Entonces, para reformularlo de nuevo: el programa nunca (se permite) atascarse, las llamadas inactivas nunca se ejecutan. Su deber lo realiza el kernel (escribir algo, esperar que algo venga por la red, esperar que transcurra el tiempo) u otro hilo o proceso. - El proceso Node verifica si el núcleo ha completado al menos una de esas tareas en la única llamada de bloqueo al sistema operativo una vez en cada ciclo de bucle de eventos. Ese punto se alcanza cuando todo se realiza sin bloqueo.
¿Claro? :-)
No se Node. ¿Pero de dónde viene el c.query?
fuente