Tengo una aplicación que se ejecuta en Nest.Js / Node.Js que procesa texto y, por eso, tiene una iteración .map (o .forEach ) que requiere muchos recursos (tokenizar una oración, luego eliminar las palabras vacías, etc.) por cada oración de las cuales puede haber decenas de miles).
Para la reproducibilidad, proporciono el código que uso a continuación, sin los detalles del procesamiento de texto, solo un bucle largo y pesado para emular mi problema:
const before = Date.now() const statements = [...Array(100)] let l = 0 let p = 0 const processed = statements.map((value, index) => { // a set of consecutive functions that take a long time to execute for (let i = 0; i < 100000; i++) { l = l + i * Math.random() } // console.log(index + ' ' + l + ' ' + (Date.now() - before)) p = index return l }) console.log(processed) const after = Date.now() console.log(p + ' results in ' + (after - before) + ' ms')Para archivos muy grandes, el proceso consume hasta el 100% de la CPU, por lo que la aplicación deja de responder.
Me preguntaba si conoce alguna solución para evitar que la aplicación tome toda la CPU disponible. ¿Hay alguna manera de configurar cualquier proceso individual para que tome el 80 % y siempre deje el 20 % para los demás usuarios?
PD: lo ejecuto desde el ECS de Amazon, por lo que, por supuesto, puedo crear clústeres y también usar PM2 para la agrupación en clústeres, pero me pregunto cómo puedo evitar la carga del 100% de un solo proceso dentro de una aplicación Javascript / Node.Js. Menciono el marco Nest.Js porque quizás también haya una solución allí.
En términos de limitar el uso del 100 % de la CPU en un solo subproceso, existen formas arquitectónicas de hacerlo a nivel de servidor; sin embargo, no creo que ese sea realmente el resultado que desearía. Una CPU que usa el 100 % no es un problema (las CPU a menudo alcanzan el 100 % de la CPU durante períodos de tiempo muy cortos para procesar las cosas lo más rápido posible), es más usar la CPU al 100 % durante un período de tiempo prolongado y evitar otras aplicaciones obtengan ciclos de CPU.
Por lo que veo en el código de ejemplo, podría ser una mejor solución usar Colas dentro de NestJS. La documentación se puede ver aquí usando Bull. De esta manera, puede utilizar los límites de velocidad de los trabajos que se procesan y modificarlos allí, y otras aplicaciones no esperarán a que se complete todo el proceso.
Por ejemplo, si tiene 100 000 archivos para procesar, es posible que desee crear un trabajo que procese 1000 de ellos a la vez y cree 100 trabajos para colocarlos en la cola. Este es un proceso bastante típico para procesos que requieren una gran cantidad de tiempo de cómputo.
Sé que esta no es exactamente la respuesta que estoy seguro de que estaba buscando, pero espero que ayude y proporcione una solución que no sea específica para su arquitectura.
JS es un lenguaje síncrono. Ahora, al usar esto en su aplicación de nodo, puede terminar bloqueando su hilo principal si no se hace correctamente.
https://nodejs.org/en/docs/guides/dont-block-the-event-loop/
Node.js tiene dos tipos de subprocesos: uno Event Loop y k Workers. Event Loop es responsable de las devoluciones de llamada de JavaScript y la E/S sin bloqueo, y un Worker ejecuta tareas correspondientes al código C++ que completa una solicitud asincrónica, incluido el bloqueo de E/S y el trabajo intensivo de la CPU. Ambos tipos de subprocesos funcionan en no más de una actividad a la vez. Si cualquier devolución de llamada o tarea lleva mucho tiempo, el subproceso que la ejecuta se bloquea. Si su aplicación realiza devoluciones de llamada o tareas de bloqueo, esto puede conducir a un rendimiento degradado (clientes/segundo) en el mejor de los casos y una denegación de servicio completa en el peor de los casos.
Para escribir un servidor web de alto rendimiento y más resistente a DoS, debe asegurarse de que ni su bucle de eventos ni sus trabajadores se bloqueen en entradas benignas y maliciosas.
Una solicitud es descargar la operación a otra área.