update node home guide español node.js

node.js - home - update node js



¿Cómo, en general, Node.js maneja 10,000 solicitudes concurrentes? (5)

Entiendo que Node.js usa un solo subproceso y un bucle de eventos para procesar solicitudes que solo procesan una a la vez (que no es de bloqueo). Pero aún así, cómo funciona eso, digamos 10,000 solicitudes simultáneas. ¿El bucle de eventos procesará todas las solicitudes? ¿No tomaría eso demasiado tiempo?

No puedo entender (todavía) cómo puede ser más rápido que un servidor web multiproceso. Entiendo que el servidor web multiproceso será más costoso en recursos (memoria, CPU), pero ¿no sería aún más rápido? Probablemente estoy equivocado; explique cómo este subproceso único es más rápido en muchas solicitudes y qué hace normalmente (en un nivel alto) cuando atiende muchas solicitudes como 10,000.

Y también, ¿ese hilo único escalará bien con esa gran cantidad? Tenga en cuenta que recién estoy empezando a aprender Node.js.


Entiendo que Node.js usa un solo subproceso y un bucle de eventos para procesar solicitudes que solo procesan una a la vez (que no es de bloqueo).

Podría estar malinterpretando lo que ha dicho aquí, pero "uno a la vez" parece que no está entendiendo completamente la arquitectura basada en eventos.

En una arquitectura de aplicación "convencional" (no controlada por eventos), el proceso pasa mucho tiempo sentado esperando que algo suceda. En una arquitectura basada en eventos como Node.js, el proceso no solo espera, sino que puede continuar con otro trabajo.

Por ejemplo: obtiene una conexión de un cliente, la acepta, lee los encabezados de la solicitud (en el caso de http), luego comienza a actuar sobre la solicitud. Puede leer el cuerpo de la solicitud, generalmente terminará enviando algunos datos al cliente (esto es una simplificación deliberada del procedimiento, solo para demostrar el punto).

En cada una de estas etapas, la mayor parte del tiempo se pasa esperando que lleguen algunos datos desde el otro extremo; el tiempo real dedicado al procesamiento en el subproceso JS principal suele ser bastante mínimo.

Cuando el estado de un objeto de E / S (como una conexión de red) cambia de manera que necesita procesamiento (por ejemplo, los datos se reciben en un socket, un socket se puede escribir, etc.), el hilo principal Node.js JS se despierta con una lista de artículos que necesitan ser procesados.

Encuentra la estructura de datos relevante y emite algún evento en esa estructura que hace que se ejecuten devoluciones de llamada, que procesan los datos entrantes o escriben más datos en un socket, etc. Una vez que se han procesado todos los objetos de E / S que necesitan procesamiento procesado, el hilo principal de Node.js JS esperará nuevamente hasta que se le indique que hay más datos disponibles (o alguna otra operación se ha completado o ha excedido el tiempo de espera).

La próxima vez que se despierte, bien podría deberse a la necesidad de procesar un objeto de E / S diferente, por ejemplo, una conexión de red diferente. Cada vez, se ejecutan las devoluciones de llamada relevantes y luego vuelve a dormir esperando que ocurra algo más.

El punto importante es que el procesamiento de diferentes solicitudes está intercalado, no procesa una solicitud de principio a fin y luego pasa a la siguiente.

En mi opinión, la principal ventaja de esto es que una solicitud lenta (por ejemplo, si está tratando de enviar 1 MB de datos de respuesta a un dispositivo de teléfono móvil a través de una conexión de datos 2G, o está haciendo una consulta de base de datos realmente lenta) no ganó '' Bloquee los más rápidos.

En un servidor web multiproceso convencional, normalmente tendrá un subproceso para cada solicitud que se maneja, y procesará SOLO esa solicitud hasta que finalice. ¿Qué sucede si tienes muchas solicitudes lentas? Termina con muchos de sus hilos dando vueltas procesando estas solicitudes, y otras solicitudes (que podrían ser solicitudes muy simples que podrían manejarse muy rápidamente) se ponen en cola detrás de ellas.

Hay muchos otros sistemas basados ​​en eventos además de Node.js, y tienden a tener ventajas y desventajas similares en comparación con el modelo convencional.

No diría que los sistemas basados ​​en eventos son más rápidos en cada situación o con cada carga de trabajo: tienden a funcionar bien para las cargas de trabajo vinculadas a E / S, no tan bien para las vinculadas a CPU.


Agregando a la respuesta de slebetman: cuando dice Node.JS puede manejar 10,000 solicitudes simultáneas, son esencialmente solicitudes sin bloqueo, es decir, estas solicitudes pertenecen principalmente a la consulta de la base de datos.

Internamente, el event loop de Node.JS de Node.JS está manejando un thread pool , donde cada subproceso maneja una non-blocking request y el bucle de eventos continúa escuchando más solicitudes después de delegar el trabajo a uno de los subprocesos del grupo de thread pool . Cuando uno de los subprocesos completa el trabajo, envía una señal al event loop que ha finalizado también con la callback . Event loop procesa esta devolución de llamada y devuelve la respuesta.

Como es nuevo en NodeJS, lea más sobre nextTick para comprender cómo funciona internamente el bucle de eventos. Lea los blogs en http://javascriptissexy.com , fueron realmente útiles para mí cuando comencé con JavaScript / NodeJS.


Lo que parece estar pensando es que la mayor parte del procesamiento se maneja en el bucle de eventos del nodo. El nodo en realidad explota el trabajo de E / S en subprocesos. Las operaciones de E / S generalmente toman órdenes de magnitud más largas que las operaciones de la CPU, entonces, ¿por qué la CPU espera eso? Además, el sistema operativo ya puede manejar las tareas de E / S muy bien. De hecho, debido a que Node no espera, logra una utilización de CPU mucho mayor.

Por analogía, piense en NodeJS como un camarero que toma los pedidos de los clientes mientras los chefs de E / S los preparan en la cocina. Otros sistemas tienen varios chefs, que toman el pedido de un cliente, preparan la comida, limpian la mesa y solo luego atienden al siguiente cliente.


Si tiene que hacer esta pregunta, probablemente no esté familiarizado con lo que hacen la mayoría de las aplicaciones / servicios web. Probablemente esté pensando que todo el software hace esto:

user do an action │ v application start processing action └──> loop ... └──> busy processing end loop └──> send result to user

Sin embargo, no es así como funcionan las aplicaciones web, o de hecho cualquier aplicación con una base de datos como back-end. Las aplicaciones web hacen esto:

user do an action │ v application start processing action └──> make database request └──> do nothing until request completes request complete └──> send result to user

En este escenario, el software pasa la mayor parte de su tiempo de ejecución utilizando un 0% de tiempo de CPU esperando que la base de datos regrese.

Aplicación de red multiproceso:

Las aplicaciones de red multiproceso manejan la carga de trabajo anterior de esta manera:

request ──> spawn thread └──> wait for database request └──> answer request request ──> spawn thread └──> wait for database request └──> answer request request ──> spawn thread └──> wait for database request └──> answer request

Entonces, el hilo pasa la mayor parte de su tiempo usando 0% de CPU esperando que la base de datos devuelva datos. Al hacerlo, han tenido que asignar la memoria requerida para un subproceso que incluye una pila de programas completamente separada para cada subproceso, etc. Además, tendrían que iniciar un subproceso que, aunque no es tan costoso como iniciar un proceso completo, todavía no es exactamente barato.

Bucle de evento de subproceso único

Dado que pasamos la mayor parte de nuestro tiempo usando 0% de CPU, ¿por qué no ejecutamos algún código cuando no estamos usando CPU? De esa manera, cada solicitud seguirá obteniendo la misma cantidad de tiempo de CPU que las aplicaciones multiproceso, pero no necesitamos iniciar un subproceso. Entonces hacemos esto:

request ──> make database request request ──> make database request request ──> make database request database request complete ──> send response database request complete ──> send response database request complete ──> send response

En la práctica, ambos enfoques devuelven datos con aproximadamente la misma latencia, ya que es el tiempo de respuesta de la base de datos el que domina el procesamiento.

La principal ventaja aquí es que no necesitamos generar un nuevo hilo, por lo que no necesitamos hacer mucho, mucho malloc, lo que nos ralentizaría.

Magia, hilos invisibles

Lo aparentemente misterioso es cómo ambos enfoques anteriores logran ejecutar la carga de trabajo en "paralelo". La respuesta es que la base de datos está enhebrada. Por lo tanto, nuestra aplicación de subproceso único está aprovechando el comportamiento de subprocesos múltiples de otro proceso: la base de datos.

Donde el enfoque de un solo hilo falla

Una aplicación de subproceso único falla mucho si necesita hacer muchos cálculos de CPU antes de devolver los datos. Ahora, no me refiero a un bucle para procesar el resultado de la base de datos. Eso sigue siendo principalmente O (n). Lo que quiero decir es cosas como hacer la transformación de Fourier (codificación mp3, por ejemplo), el trazado de rayos (representación 3D), etc.

Otro escollo de las aplicaciones de subproceso único es que solo utilizará un solo núcleo de CPU. Entonces, si tiene un servidor de cuatro núcleos (no es raro hoy en día), no está utilizando los otros 3 núcleos.

Donde el enfoque multiproceso falla

Una aplicación multiproceso falla mucho si necesita asignar mucha RAM por hilo. Primero, el uso de RAM en sí mismo significa que no puede manejar tantas solicitudes como una aplicación de subproceso único. Peor aún, malloc es lento. La asignación de montones y montones de objetos (lo cual es común para los marcos web modernos) significa que potencialmente podemos llegar a ser más lentos que las aplicaciones de subproceso único. Aquí es donde node.js generalmente gana.

Un caso de uso que termina empeorando la multiproceso es cuando necesita ejecutar otro lenguaje de script en su hilo. Primero, generalmente, necesita malloc el tiempo de ejecución completo para ese idioma, luego necesita malloc las variables utilizadas por su script.

Entonces, si está escribiendo aplicaciones de red en C o go o java, la sobrecarga de subprocesos generalmente no será tan mala. Si está escribiendo un servidor web C para servir PHP o Ruby, entonces es muy fácil escribir un servidor más rápido en javascript o Ruby o Python.

Enfoque híbrido

Algunos servidores web utilizan un enfoque híbrido. Nginx y Apache2, por ejemplo, implementan su código de procesamiento de red como un grupo de subprocesos de bucles de eventos. Cada subproceso ejecuta un bucle de eventos procesando simultáneamente solicitudes de un solo subproceso, pero las solicitudes tienen equilibrio de carga entre múltiples subprocesos.

Algunas arquitecturas de un solo subproceso también utilizan un enfoque híbrido. En lugar de iniciar múltiples subprocesos desde un solo proceso, puede iniciar múltiples aplicaciones, por ejemplo, 4 servidores node.js en una máquina de cuatro núcleos. Luego, usa un equilibrador de carga para distribuir la carga de trabajo entre los procesos.

En efecto, los dos enfoques son imágenes especulares técnicamente idénticas entre sí.


Pasos de procesamiento del modelo de bucle de evento de subproceso único:

  • Clientes Enviar solicitud al servidor web.

  • El servidor web Node JS mantiene internamente un grupo de subprocesos limitados para proporcionar servicios a las solicitudes del cliente.

  • El servidor web Node JS recibe esas solicitudes y las coloca en una cola. Se conoce como "Cola de eventos".

  • El servidor web del nodo JS tiene internamente un componente, conocido como "bucle de eventos". Por qué obtuvo este nombre es que usa un bucle indefinido para recibir solicitudes y procesarlas.

  • Event Loop usa solo un subproceso. Es el corazón principal del modelo de procesamiento de plataforma Node JS.

  • Incluso Loop verifica que cualquier solicitud de cliente se coloque en la cola de eventos. En caso negativo, espere las solicitudes entrantes indefinidamente.

  • En caso afirmativo, recoja una solicitud de cliente de la cola de eventos

    1. Inicia el proceso de solicitud del cliente
    2. Si esa solicitud del cliente no requiere ninguna operación de bloqueo de E / S, procese todo, prepare la respuesta y envíela de vuelta al cliente.
    3. Si esa solicitud del cliente requiere algunas operaciones de bloqueo de E / S, como interactuar con la base de datos, el sistema de archivos y los servicios externos, seguirá un enfoque diferente
  • Comprueba la disponibilidad de subprocesos desde el conjunto de subprocesos internos

  • Recoge un hilo y asigna esta solicitud de cliente a ese hilo.
  • Ese subproceso es responsable de tomar esa solicitud, procesarla, realizar operaciones de bloqueo de E / S, preparar la respuesta y enviarla nuevamente al bucle de eventos

    Muy bien explicado por @Rambabu Posa para obtener más explicaciones, vaya a lanzar este Link