Nodo.js y solicitudes intensivas de CPU


He empezado a retocar con Node.servidor HTTP js y realmente me gusta escribir Javascript del lado del servidor, pero algo me impide comenzar a usar Node.js para mi aplicación web.

Entiendo todo el concepto de E/S asincrónica, pero estoy algo preocupado por los casos extremos donde el código de procedimiento es muy intensivo en CPU, como la manipulación de imágenes o la clasificación de grandes conjuntos de datos.

Según lo entiendo, el servidor será muy rápido para solicitudes simples de páginas web, como ver un listado de usuarios o visualización de una publicación de blog. Sin embargo, si quiero escribir código muy intensivo de CPU (en el back-end de administración, por ejemplo) que genere gráficos o cambie el tamaño de miles de imágenes, la solicitud será muy lenta (unos segundos). Dado que este código no es asincrónico, todas las solicitudes que lleguen al servidor durante esos pocos segundos se bloquearán hasta que se complete mi solicitud lenta.

Una sugerencia fue usar Web Workers para tareas intensivas de CPU. Sin embargo, me temo que los trabajadores web harán que sea difícil escribir código limpio ya que funciona mediante la inclusión de un archivo JS separado. ¿Qué pasa si el código intensivo de CPU se encuentra en el método de un objeto? Es una especie de mierda para escribir un archivo JS para cada método que es intensivo de CPU.

Otra sugerencia fue generar un proceso hijo, pero eso hace que el código sea aún menos mantenible.

¿Alguna sugerencia para superar este obstáculo (percibido)? Cómo escribir código limpio orientado a objetos con Node.js mientras se asegura de que las tareas pesadas de la CPU se ejecuten async?

Author: Olivier Lalonde, 2010-08-16

5 answers

¡Lo que necesitas es una cola de tareas! Mover sus tareas de larga duración fuera del servidor web es algo bueno. Mantener cada tarea en un archivo js" separado " promueve la modularidad y la reutilización del código. Te obliga a pensar en cómo estructurar tu programa de una manera que facilite la depuración y el mantenimiento a largo plazo. Otra ventaja de una cola de tareas es que los trabajadores se pueden escribir en un idioma diferente. Simplemente haz una tarea, haz el trabajo y escribe la respuesta.

Algo como esto https://github.com/resque/resque

Aquí hay un artículo de github sobre por qué lo construyeron http://github.com/blog/542-introducing-resque

 47
Author: Tim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-05 12:27:20

Esto es un malentendido de la definición de servidor web only solo debe usarse para "hablar" con los clientes. Las tareas de carga pesada deben delegarse a programas independientes (que, por supuesto, también pueden escribirse en JS).
Probablemente dirías que está sucio, pero te aseguro que un proceso de servidor web atascado en cambiar el tamaño de las imágenes es peor (incluso para digamos Apache, cuando no bloquea otras consultas). Aún así, puede usar una biblioteca común para evitar la redundancia de código.

EDITAR: Tengo llegar a una analogía; aplicación web debe ser como un restaurante. Tienes camareros (servidor web) y cocineros (trabajadores). Los camareros están en contacto con los clientes y hacen tareas simples como proporcionar menú o explicar si algún plato es vegetariano. Por otro lado, delegan tareas más difíciles a la cocina. Debido a que los camareros solo hacen cosas simples, responden rápidamente, y los cocineros pueden concentrarse en su trabajo.

Nodo

.js aquí sería un camarero único pero muy talentoso que puede procesar muchos solicitudes a la vez, y Apache sería una banda de camareros tontos que solo procesan una solicitud cada uno. Si este Nodo.js camarero comenzaría a cocinar, sería una catástrofe inmediata. Aún así, cocinar también podría agotar incluso una gran cantidad de camareros apache, sin mencionar el caos en la cocina y la disminución progresiva de la responsabilidad.

 271
Author: mbq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-16 14:24:47

No quieres que tu código intensivo de CPU ejecute async, quieres que ejecute en paralelo. Necesita obtener el trabajo de procesamiento del subproceso que está sirviendo solicitudes HTTP. Es la única manera de resolver este problema. Con NodeJS la respuesta es el módulo de clúster , para generar procesos hijos para hacer el trabajo pesado. (El nodo AFAIK no tiene ningún concepto de hilos / memoria compartida; son procesos o nada). Tiene dos opciones para estructurar su aplicación. Puede obtener la solución 80/20 generando 8 servidores HTTP y manejando tareas de computación intensiva de forma sincrónica en los procesos secundarios. Hacer eso es bastante simple. Podrías tomarte una hora para leer sobre ello en ese enlace. De hecho, si usted acaba de copiar el código de ejemplo en la parte superior de ese enlace se obtendrá 95% del camino allí.

La otra forma de estructurar esto es configurar una cola de trabajos y enviar grandes tareas de cómputo sobre la cola. Tenga en cuenta que hay una gran cantidad de gastos generales asociados con la IPC para una cola de trabajos, por lo que esto solo es útil cuando las tareas son considerablemente más grandes que la sobrecarga.

Me sorprende que ninguna de estas otras respuestas siquiera mencione cluster.

Fondo: El código asíncrono es un código que se suspende hasta que algo sucede en otro lugar, momento en el que el código se despierta y continúa la ejecución. Un caso muy común donde algo lento debe suceder en otro lugar es I/O.

El código asíncrono no es útil si su procesador que es responsable de hacer el trabajo. Ese es precisamente el caso de las tareas "intensivas en cómputo".

Ahora, puede parecer que el código asincrónico es un nicho, pero de hecho es muy común. Simplemente sucede que no es útil para tareas intensivas de cómputo.

Esperar E/S es un patrón que siempre ocurre en servidores web, por ejemplo. Cada cliente que se conecta a su servidor obtiene un socket. La mayoría de las veces los enchufes están vacíos. No quieres hacer nada hasta que un socket recibe algunos datos, momento en el que desea manejar la solicitud. Bajo el capó de un servidor HTTP como Nodo está utilizando una biblioteca de eventos (libev) para realizar un seguimiento de los miles de sockets abiertos. El sistema operativo notifica a libev, y luego libev notifica a NodeJS cuando uno de los sockets obtiene datos, y luego NodeJS coloca un evento en la cola de eventos, y su código http se inicia en este punto y maneja los eventos uno tras otro. Los eventos no se ponen en la cola hasta que el socket tiene algunos datos, por lo que los eventos nunca esperan datos , ya están ahí para ellos.

Los servidores web basados en eventos de subproceso único tienen sentido como paradigma cuando el cuello de botella está esperando un montón de conexiones de sockets en su mayoría vacías y no desea un subproceso o proceso completo para cada conexión inactiva y no desea sondear sus sockets de 250k para encontrar el siguiente que tenga datos en él.

 10
Author: masonk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-17 00:51:23

Un par de enfoques que puede utilizar.

Como señala @Tim, puede crear una tarea asíncrona que se encuentre fuera o paralela a su lógica de servicio principal. Depende de sus requisitos exactos, pero incluso cron puede actuar como un mecanismo de cola.

WebWorkers puede trabajar para sus procesos asincrónicos, pero actualmente no son compatibles con node.js. Hay un par de extensiones que proporcionan soporte, por ejemplo: http://github.com/cramforce/node-worker

Todavía get aún puede reutilizar módulos y código a través del mecanismo estándar "requiere". Solo necesita asegurarse de que el envío inicial al trabajador pase toda la información necesaria para procesar los resultados.

 7
Author: Toby Hede,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-17 22:56:18

Use child_process es una solución. Pero cada proceso hijo generado puede consumir mucha memoria en comparación con Go goroutines

También puede usar una solución basada en colas como kue

 0
Author: neo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-20 10:27:21