Comencé a jugar con el servidor HTTP Node.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 asíncrona, pero estoy algo preocupado por los casos extremos en los que el código de procedimiento requiere mucha CPU, como la manipulación de imágenes o la clasificación de grandes conjuntos de datos.
Según tengo entendido, el servidor será muy rápido para solicitudes simples de páginas web, como ver una lista de usuarios o ver una publicación de blog. Sin embargo, si quiero escribir un código muy intensivo de CPU (en el back-end de administración, por ejemplo) que genera gráficos o redimensiona miles de imágenes, la solicitud será muy lenta (unos segundos). Dado que este código no es asíncrono, todas las solicitudes que lleguen al servidor durante esos pocos segundos se bloquearán hasta que finalice 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 al incluir un archivo JS separado. ¿Qué sucede si el código intensivo de la CPU se encuentra en el método de un objeto? Es un poco malo escribir un archivo JS para cada método que requiere mucha 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 se escribe código limpio orientado a objetos con Node.js mientras se asegura de que las tareas pesadas de la CPU se ejecuten de forma asíncrona?
fuente
Respuestas:
¡Lo que necesitas es una cola de tareas! Mover sus tareas de larga duración fuera del servidor web es una BUENA cosa. 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. Otro beneficio de una cola de tareas es que los trabajadores pueden estar escritos en un idioma diferente. Simplemente haga una tarea, haga el trabajo y escriba la respuesta.
algo así https://github.com/resque/resque
Aquí hay un artículo de github sobre por qué lo construyeron http://github.com/blog/542-introducing-resque
fuente
Esto es un malentendido de la definición de servidor web: solo debe usarse para "hablar" con los clientes. Las tareas de carga pesada deben delegarse en programas independientes (eso, por supuesto, también se puede escribir en JS).
Probablemente diría que está sucio, pero le aseguro que un proceso de servidor web atascado en cambiar el tamaño de las imágenes es peor (incluso para decir Apache, cuando no bloquea otras consultas). Aún así, puede usar una biblioteca común para evitar la redundancia de código.
EDITAR: se me ocurrió una analogía; La 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 realizan tareas simples como proporcionar un 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.
Node.js aquí sería un camarero único pero muy talentoso que puede procesar muchas solicitudes a la vez, y Apache sería una pandilla de camareros tontos que solo procesan una solicitud cada uno. Si este mesero de Node.js comenzara a cocinar, sería una catástrofe inmediata. Aún así, la cocina 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 capacidad de respuesta.
fuente
No desea que el código intensivo de su CPU se ejecute de forma asíncrona, desea que se ejecute en paralelo . Debe obtener el trabajo de procesamiento del hilo que atiende las solicitudes HTTP. Es la única forma de resolver este problema. Con NodeJS la respuesta es el módulo de clúster, para desovar procesos infantiles para hacer el trabajo pesado. (El nodo AFAIK no tiene ningún concepto de hilos / memoria compartida; es 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 intensivas en cómputo sincrónicamente en los procesos secundarios. Hacer eso es bastante simple. Podrías tomarte una hora para leer sobre eso en ese enlace. De hecho, si simplemente arranca el código de ejemplo en la parte superior de ese enlace, obtendrá el 95% del camino.
La otra forma de estructurar esto es configurar una cola de trabajos y enviar grandes tareas de cómputo a través de la cola. Tenga en cuenta que hay una gran sobrecarga asociada con el 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 mencione clúster.
Antecedentes: el código asincrónico es un código que se suspende hasta que algo sucede en otro lugar , momento en el que el código se activa y continúa la ejecución. Un caso muy común en el que algo lento debe suceder en otro lugar es la E / S.
El código asincrónico no es útil si es su procesador el responsable de hacer el trabajo. Ese es precisamente el caso de las tareas de "cálculo intensivo".
Ahora, puede parecer que el código asincrónico es un nicho, pero de hecho es muy común. Simplemente no es útil para calcular tareas intensivas.
Esperar en E / S es un patrón que siempre ocurre en los 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 desea hacer nada hasta que un socket reciba algunos datos, momento en el que desea manejar la solicitud. Bajo el capó, un servidor HTTP como Node 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 activa en este punto y maneja los eventos uno tras otro. Los eventos no se ponen en la cola hasta que el socket tenga algunos datos, por lo que los eventos nunca esperan datos, ya están ahí para ellos.
Los servidores web basados en eventos de un solo subproceso tienen sentido como paradigma cuando el cuello de botella está esperando en un montón de conexiones de socket en su mayoría vacías y no desea un hilo o proceso completo para cada conexión inactiva y no desea sondear sus 250k sockets para encontrar el siguiente que tenga datos.
fuente
Un par de enfoques que puedes usar.
Como señala @Tim, puede crear una tarea asincrónica 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.
Los WebWorkers pueden funcionar para sus procesos asíncronos, pero actualmente no son compatibles con node.js. Hay un par de extensiones que brindan soporte, por ejemplo: http://github.com/cramforce/node-worker
Usted todavía puede reutilizar módulos y códigos 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.
fuente
El uso
child_process
es una solución. Pero cada proceso secundario generado puede consumir mucha memoria en comparación con Gogoroutines
También puede usar soluciones basadas en colas como kue
fuente