Introducción
Los runtimes proporcionados por SAP Cloud Platform, especialmente los disponibles en el entorno de Cloud Foundry a través del concepto de buildpacks, ofrecen a los arquitectos técnicos y desarrolladores de aplicaciones una amplia variedad de opciones en cuanto a la elección de lenguaje de programación, tiempo de ejecución del servidor y framework. Para ciertos runtimes, existen casos de uso claros y reconocidos, pero para otros la elección no es tan directa. Probablemente, una de las discusiones y debates más intensos que tienen lugar desde hace muchos años y que es muy relevante para SAP Cloud Platform, es la elección entre JavaScript del lado del servidor y Java. Para ser más específico, comúnmente las discusiones se centran no tanto en los lenguajes de programación en sí, sino en las aplicaciones desarrolladas utilizando frameworks para el runtime de Node.js y las aplicaciones desarrolladas utilizando el framework Java Spring. En muchos materiales que abordan este tema, las aplicaciones Java suelen considerarse más adecuadas para operaciones complejas que potencialmente requieren un alto nivel de paralelización (gracias a la multihilo de la JVM), pero el costo de esto es un mayor consumo de recursos (especialmente en lo que respecta al consumo de memoria de la JVM subyacente), mientras que las aplicaciones Node.js se consideran más livianas y adecuadas para operaciones no intensivas en CPU y no bloqueantes (gracias a los mecanismos de concurrencia de Node.js y la naturaleza no bloqueante de JavaScript). Java solía estar asociado comúnmente con un modelo de programación imperativo clásico (bloqueante) implementado en un runtime multihilo, mientras que Node.js estaba asociado con un modelo de programación no bloqueante implementado en un runtime de un solo hilo con soporte de concurrencia. Pero ambas partes evolucionaron con el tiempo: los principios de programación reactiva (no bloqueante) fueron introducidos en Java y se empezaron a admitir en versiones posteriores de Java y sus frameworks, Node.js recibió módulos que brindan soporte de multihilo...
En este blog, me gustaría reflexionar sobre este tema y hacer una breve comparación de dos aplicaciones livianas: una escrita en Java, basada en Java Spring Boot y que utiliza módulos Spring Reactive, y la otra escrita en JavaScript, basada en Node.js y que utiliza el framework Express. Ambas aplicaciones utilizan la misma capa de persistencia (base de datos NoSQL - MongoDB), implementan la misma lógica de consulta y han sido desplegadas en SAP Cloud Platform, entorno de Cloud Foundry utilizando contenedores de tamaño similar y buildpacks predeterminados (java_buildpack y nodejs_buildpack, respectivamente). Las aplicaciones exponen una API REST que puede ser consumida para consultar los documentos correspondientes almacenados en el repositorio de MongoDB. Para las pruebas de carga, voy a utilizar Apache JMeter que producirá un gran número de solicitudes paralelas e invocará las aplicaciones bajo prueba.
El ejercicio no tiene como objetivo comparar el consumo de recursos de las aplicaciones (como se mencionó anteriormente, las aplicaciones Java suelen tener un mayor consumo inicial de memoria que las aplicaciones Node.js), ni tampoco comparar los patrones de consumo de recursos de estas aplicaciones cuando se someten a pruebas de carga (este análisis por sí solo merece un blog separado detallado), sino que me voy a centrar en una sola métrica - el rendimiento.
El blog fue inspirado por las comparaciones entre el rendimiento de las aplicaciones Node.js y Java desplegadas en Cloud Foundry (por ejemplo, consulte
el blog
escrito por
mariusobert
), así como otras comparaciones sobre el tema, que se publican en las comunidades de Java y Node.js.
Visión general y notas
Se presenta un resumen de alto nivel y un diagrama de componentes que muestra los componentes involucrados empleados en la demostración a continuación:
La comparación entre una aplicación Node.js no bloqueante y una aplicación Java bloqueante en términos de operaciones de E/S no me parece correcta, por lo que vamos a mantener la paridad aquí y comparar la aplicación Node.js con la aplicación Java basada en principios reactivos.
Otro aspecto importante es que ambas aplicaciones no implementan una lógica de aplicación compleja y siguen siendo muy livianas (lo que también resulta en la ausencia de uso de algunos patrones de abstracción que comúnmente están presentes en aplicaciones de grado de producción) - en última instancia, las aplicaciones solo van a implementar una lógica de enrutador y controlador muy básica para consultar documentos en el repositorio de MongoDB.
Junto con esto, algunos módulos y funcionalidades importantes que deberían estar presentes en aplicaciones de grado de producción - por ejemplo, autenticación y autorización, registro, manejo exhaustivo de excepciones, etc. - se eliminan intencionalmente para mantener las aplicaciones de demostración lo más simples posible.
Aplicaciones bajo prueba
La aplicación Java Spring Boot Reactive reutiliza una aplicación de muestra que se desarrolló anteriormente al demostrar la migración de una aplicación Spring Boot de un modelo imperativo clásico a un modelo reactivo, así que consulte
mi blog anterior
, si desea conocer más detalles sobre esa aplicación. La aplicación se basa