"No bloquees el bucle de eventos..." - probablemente hayas oído esta frase muchas veces... No me sorprende porque es una de las premisas más importantes cuando se trabaja con Node. Pero también hay una segunda "cosa" que deberías abstenerte de bloquear - el Worker Pool. Si se descuida, puede tener un impacto significativo en el rendimiento de la aplicación e incluso en su seguridad.
Hilos
Lo principal a recordar: hay dos tipos de hilos en Node.js: Main Thread - que es manejado por Bucle de eventosy Pool de trabajadores (pool de hilos) - que es el pool de hilos -
gracias a libuv. Cada uno de ellos tiene un trabajo diferente. El objetivo del primero es gestionar las operaciones de E/S no bloqueantes, y el segundo es responsable del trabajo intensivo de la CPU y también de las E/S bloqueantes.

Pero, ¿qué es un hilo y en qué se diferencia de un proceso? Hay varias diferencias, pero la más importante para nosotros es cómo se les asigna la memoria. Puedes pensar en un proceso como en una aplicación. Dentro de cada proceso, hay un trozo de memoria dedicado sólo para este proceso. Así, un proceso no tiene acceso a la memoria del segundo, y esta propiedad garantiza una alta seguridad. Para establecer comunicación entre ellos, debemos hacer algún trabajo. Los hilos son diferentes. Los hilos se ejecutan dentro de un proceso y comparten la misma memoria, por lo que no hay ningún problema en que los hilos compartan datos.
Sin embargo, hay una cuestión que causa problemas. Se llama condición de carrera. Los hilos pueden ejecutarse al mismo tiempo, así que ¿cómo sabemos cuál termina primero? Puede ocurrir que la primera vez que lo ejecutes, la primera operación termine primero, y la próxima vez puede resultar lo contrario y la segunda operación termine antes que la primera. ¡Imagínate trabajar con operaciones de escritura/lectura en esas condiciones! ¡Una pesadilla! A veces es muy difícil escribir correctamente código en un entorno multihilo.
Además, los lenguajes multihilo tienen una gran sobrecarga de memoria porque crean un hilo distinto para cada petición; así, si quieres llamar a 1000 peticiones, crean 1000 hilos.
¿Cómo solucionar este problema? Utilizando un único hilo. Y eso es lo que Nodo te ofrece.

Como JavaScript desarrollador Le animo a que vea el película
en la que Bart Belder explica claramente el concepto de bucle de eventos. El diagrama anterior está tomado de su presentación. Y si no conoce estos términos en absoluto, tanto Nodo y Libuv tienen una documentación excelente 🙂 .
Acerca del bloqueo
En JavaScript desarrollo industria dicen que porque Nodo es monohilo y no bloqueante, se puede lograr una mayor concurrencia con los mismos recursos que con las soluciones multihilo. Es cierto, pero no es tan bonito y fácil como parece.
Desde Node.js es single-threaded (parte JS), las tareas intensivas de CPU bloquearán todas las peticiones en curso hasta que se complete la tarea en particular. Por lo tanto, es cierto que en Node.js puedes bloquear todas las peticiones sólo porque una de ellas contenía una instrucción de bloqueo. El código bloqueante significa que tarda más de unos milisegundos en terminar. Pero no confunda un tiempo de respuesta largo con bloqueo. La respuesta de la base de datos puede tomar mucho tiempo, pero no bloquea su proceso (aplicación).
Los métodos bloqueantes se ejecutan de forma sincrónica y los no bloqueantes de forma asincrónica.
¿Cómo puedes ralentizar (o bloquear) tu bucle de eventos?
- regex vulnerable - una expresión regular vulnerable es aquella en la que su motor de expresiones regulares puede tardar un tiempo exponencial; puede leer más sobre ellas aquí,
- Operaciones JSON en objetos grandes,
- utilizando las API síncronas de Nodo en lugar de versiones asíncronas; todos los métodos de E/S de la biblioteca estándar Node.js ofrecen también sus versiones asíncronas,
- otros errores de programación, como los bucles infinitos síncronos.
En ese caso, dado que el Worker Pool utiliza un pool de hilos, ¿es posible bloquearlos también? Lamentablemente, sí 🙁 . Nodo se basa en una filosofía un hilo para muchos clientes.
Supongamos que una determinada tarea realizada por un Worker específico es muy compleja y necesita más tiempo para ser terminada. Como resultado, el Worker se bloquea y no puede ser utilizado para ejecutar ninguna de las otras tareas pendientes hasta que sus instrucciones sean ejecutadas. Como probablemente ya habrás adivinado, esto puede afectar al rendimiento. Puedes prevenir estos problemas minimizando la variación en los tiempos de las Tareas mediante el uso de la partición de Tareas.
Conclusión
Evita el bloqueo, eso seguro. Si sólo puedes, elige siempre versiones asíncronas de las API de la biblioteca estándar. De lo contrario, después de ejecutar su aplicación, el cliente puede experimentar varios problemas, empezando por el rendimiento degradado y terminando en la retirada completa, que es fatal desde la perspectiva del usuario.
Más información:
Por qué debería (probablemente) utilizar Typescript
¿Cómo no matar un proyecto con malas prácticas de codificación?
Estrategias de obtención de datos en NextJS