Al agregar subprocesos de trabajo a sus servidores Node.js, Wix redujo el uso de pods de Kubernetes en aproximadamente un 70 % y le dio al servicio de creación de sitios web las estadísticas que demuestran que Node.js es adecuado para el trabajo intensivo de CPU con alto rendimiento.
Wix ejecuta operaciones de JavaScript con uso intensivo de la CPU en el hilo único de Node.js como parte de su plataforma de ejecución de representación del lado del servidor (SSRE). SSRE es una plataforma de ejecución de código multipropósito que renderiza React.js del lado del servidor.
Pero un solo hilo no puede hacer mucho, como explica el desarrollador de software Guy Treger en su última publicación de blog. Una vez que el tráfico alcanzó un total de 1 millón de RPM, se requirió una cantidad «más que aceptada» de pods de Kubernetes de producción para servirlo correctamente.
Y de repente hubo un problema de inmanejabilidad. Demasiados pods y un alto volumen de tareas de uso intensivo de la CPU que se ejecutan en un solo subproceso integrado en la arquitectura de Wix. ¿La solución más lógica? Agregue más subprocesos para separar la carga de trabajo.
Dado que el objetivo es siempre más tráfico, no menos, los ingenieros de la empresa necesitaban una solución nueva y escalable. Y es un caso raro que una empresa pueda reconstruir toda la arquitectura y este no fue un ejemplo. Lo mejor que se podía hacer era agregar más hilos, ya que era el único hilo el que causaba más dolor. El objetivo de agregar más subprocesos era descargar el trabajo a otras unidades de procesamiento para permitir que varios subprocesos se ejecutaran parcialmente en hardware que contenía varios núcleos de CPU.
Las capacidades de multiprocesamiento integradas de Node.js eran «exageradas» para lo que buscaba el equipo de ingeniería de Wix. Estaban buscando una solución que requiriera menos recursos, mantenimiento y orquestación.
subprocesos de trabajo
De acuerdo con los documentos de Node.js:
El módulo worker_threads permite el uso de subprocesos que ejecutan JavaScript en paralelo. Los trabajadores (subprocesos) son útiles para ejecutar operaciones de JavaScript con uso intensivo de la CPU. No ayudan mucho con el trabajo intensivo de E/S. Las operaciones de E/S asincrónicas integradas en Node.js son más eficientes que los trabajadores.
A diferencia de child_processes o cluster, worker_threads puede compartir memoria.
Node.js es una nueva característica que se volverá estable en la versión 14 (LTS) y se lanzó en octubre de 2020. Si bien Node.js tiene soporte nativo, es bastante nuevo e inmaduro. Para que Wix implementara completamente el código a nivel de producción, los ingenieros de la empresa tuvieron que agregar paquetes de código abierto adicionales. Originalmente buscaron un paquete para unir todo, pero descubrieron que agregar paquetes individuales funcionaba mejor para sus necesidades.
Obstáculos y soluciones de código abierto
Al presentar los subprocesos múltiples de Node.js, el equipo de Wix enfrentó dos obstáculos importantes: las funciones del grupo de tareas y la compatibilidad con la comunicación entre subprocesos.
Características del grupo de tareas
Listo para usar: cree subprocesos de trabajo manualmente y mantenga el ciclo de vida manualmente.
Obstáculo: Asegurarse constantemente de que haya suficiente para recrear subprocesos de trabajo cuando mueren, implementar varios tiempos de espera y manejar todas las demás tareas de mantenimiento manual es una tarea importante.
Solución de código abierto: grupo genérico (npmjs): los resultados del grupo de subprocesos se lograron al agregar esta popular API de grupo.
Comunicación entre subprocesos similar a RPC
Listo para usar: los subprocesos pueden comunicarse entre sí (por ejemplo, los subprocesos principales y sus trabajadores generados) mediante una técnica de mensajería asincrónica.
Obstáculo: lidiar con la mensajería haría que el código fuera más difícil de usar y mantener. Los ingenieros buscaban un paquete que permitiera a los subprocesos llamar a «un método» en otro subproceso y obtener resultados de forma asíncrona.
Solución de código abierto: comlink (npmjs): la comunicación entre subprocesos en el código se ha vuelto más considerada y elegante con la adición de este paquete. Este paquete es conocido por su comunicación RPC en el navegador con trabajadores web JS establecidos desde hace mucho tiempo. Recientemente se agregó compatibilidad con los trabajadores de Node.js.
El código con todos los paquetes es similar a la imagen de abajo.
El uso a nivel del servidor web se parece a la imagen de abajo.
Resultados y conclusiones
De hecho, Node.js es adecuado para servicios de alto rendimiento vinculados a la CPU. Se ha reducido la gestión de la infraestructura general. Su modesto objetivo se vio ensombrecido por ganancias significativas. El recuento de pods de SSRE disminuyó en ~70 %, mientras que las RPM por pod mejoraron en un 153 %. Hubo un SLA mejor y una aplicación más estable, con una caída del tiempo de respuesta de p50 de ~11 % y una caída de ~20 % del tiempo de respuesta de p95. La tasa de error se redujo 10 veces. Los costos computacionales directos de SSRE disminuyeron aproximadamente un 21%.
Valió la pena, pero no fue fácil. Agregar worker_threads a un entorno existente no es una simple adición. A veces se requiere una refactorización considerable porque los objetos lógicos (funciones de JavaScript) no se pueden pasar de un hilo a otro. Defina y personalice API claras, concretas y solo de datos en código para la comunicación entre trabajadores.
Ahora que se implementó un sistema que funciona, el equipo de Wix está trabajando para optimizarlo. Las áreas en las que se explora la optimización incluyen la refactorización de aplicaciones para que los trabajadores trabajen con CPU pura, explorar el uso compartido de memoria para evitar la clonación de objetos grandes entre subprocesos y encontrar la cantidad óptima de núcleos de CPU por máquina para permitir grupos de subprocesos de tamaño no constante. También está explorando el potencial de aplicar esta solución a otras aplicaciones importantes basadas en Node.js en Wix.
Grupo Creado con Sketch.