Blog

De Spring Boot a Quarkus: logrando mayor elasticidad en cloud native

Blog
Cómo Quarkus resulta una opción superadora a la hora de trabajar con frameworks para cloud native. Te contamos sus beneficios, y cómo facilita la experiencia de desarrollo.

A lo largo del tiempo hemos presenciado la aparición de diversas tecnologías, prácticas, frameworks, filosofías, etc. para poder desarrollar y distribuir software de manera rápida, minimizando los costos (tanto de su creación como de su mantenimiento y escala).

“Build once, run everywhere” (Java Virtual Machine), application servers, contenedores y orquestadores, API gateways, microservicios… y la lista continúa.

Uno de estos avances de la industria nos hizo llegar al concepto de cloud native. La Cloud Native Computing Foundation (o CNCF), una organización parte de la fundación Linux, dice:

«La computación cloud native utiliza software open source para desplegar aplicaciones como microservicios, empaquetando cada parte en su propio contenedor, y dinámicamente orquestando esos contenedores para optimizar la utilización de recursos.”

En redbee aplicamos gran parte de las recomendaciones de la CNCF, pero hay una que venimos demorando desde hace un tiempo. Nuestro caballo de batalla a la hora de desarrollar nuestras aplicaciones es Spring Boot con Java (aunque últimamente hemos estado utilizando Kotlin mucho más extensamente).

Spring Boot es un framework probado y que siempre nos ha resultado, con un ecosistema gigantesco, y librerías (muchas de las cuales cumplen con la filosofía cloud que predica la CNCF) que nos ayudan con la gran mayoría de las soluciones que planteamos. Pero, y no decimos nada nuevo, es pesado; las aplicaciones Spring Boot no cumplen con los requisitos de elasticidad que se espera en el mundo cloud native.

De Spring Boot a Quarkus

Es en este punto en el que Quarkus entra en juego. Quarkus es un framework similar a Spring Boot que fue creado de acuerdo a los principios de cloud native. Recorriendo rápidamente algunas publicaciones en internet, las menciones sobre este framework hacen referencia a, principalmente, los siguientes puntos:

  1. Ventajas para el desarrollo cloud native.
  2. Developer eXperience (DX).
  3. Programación reactiva


A continuación, profundizaremos en cada uno de estos puntos.

Desarrollo cloud native con Quarkus

Éste es uno de los aspectos más interesantes para analizar. Quarkus puede compilarse a código nativo mediante GraalVM. Hacer esto no es trivial, ya que muchas librerías Java utilizan técnicas de reflection para proveer ciertas funcionalidades. Es decir, hay comportamiento de una aplicación que sólo es conocido en tiempo de ejecución y no al momento de compilarse.

GraalVM puede analizar el código estático para generar un binario pero necesita, adicionalmente, recolectar metadata sobre el comportamiento en tiempo de ejecución. La forma de recopilar esa metadata es mediante un agente que registra información a partir de tests que recorren todo el comportamiento dinámico.

La forma de recopilar esa metadata es mediante un agente que registra información a partir de tests que recorren todo el comportamiento dinámico. GraalVM provee documentación oficial sobre cómo utilizar este agente, mientras que Quarkus pone a disposición un gran número de librerías (muchas de las más reconocidas y más utilizadas en el desarrollo Java) ya examinadas, quitándole la carga de inspeccionarlas a cada uno de los desarrolladores.

El resultado es que podemos obtener aplicaciones nativas de manera directa, utilizando lenguajes, librerías y herramientas ya conocidas para los programadores Java, lo que implica una curva de aprendizaje más suave con respecto a otra que requiera el aprendizaje de otros stacks tecnológicos completos, como los de Golang o Rust.

En este artículo de David Sánchez para Horizons, se cuenta su experiencia con Quarkus dentro de Carrefour España y se ofrece una comparativa entre una aplicación nativa y una con Spring Boot. Para rendimientos similares en términos de cantidad de transacciones por segundo y tiempo de respuesta, las diferencias se manifiestan en los siguientes KPIs:

KPINativo (Quarkus)Spring Boot
Tiempo de inicio (seg.)0,01613,85
Uso de memoria (MB)20,7108
Tamaño de la aplicación (MB)3,153

La diferencia más notoria se vislumbra en el tiempo que demora en iniciar la aplicación, lo que resulta determinante en el contexto cloud. ¿Cuántas veces hemos visto microservicios que demoran hasta 2 minutos (o incluso más) en iniciar? No es raro ver un contenedor caer en un nodo extremadamente sobrevendido, demorar más de lo esperado, fallar el healthcheck y ser reiniciado antes de poder completar su arranque.

La cantidad de tiempo perdido por desarrolladores y SREs haciendo malabares al configurar los recursos de hardware disponibles servicio por servicio es aterradora. Los tiempos muertos esperando el despliegue de servicios también llevan a la pérdida del developer flow state; es decir, que los programadores se distraen haciendo otra cosa en el mientras tanto y pierden concentración en sus tareas, con la consecuente caída en su rendimiento.

Quarkus nos regala un alivio al posibilitarnos desarrollar aplicaciones más elásticas que permitan validar su configuración y funcionamiento de forma inmediata, preservando el developer flow state.

Developer eXperience con Quarkus

Que la menor demanda de recursos por parte de las aplicaciones nativas contribuya a no perder el developer flow state es estupendo, pero sigue siendo un efecto secundario. Donde Quarkus verdaderamente contribuye en este aspecto es a través de las distintas facilidades para mejorar la experiencia del desarrollador (Developer eXperience). Veamos en detalle cada una:

Live reload en Quarkus

Si hay algo que es repetitivo al momento de programar es la secuencia Modificar código → Guardar cambios → Recompilar → Ejecutar. El framework se ocupa de hacerlo desde el vamos, sin ninguna configuración adicional.

Continuous testing en Quarkus

Apalancado sobre el live reload, cada ciclo también dispara la ejecución de los tests. Esto viene habilitado por omisión, pero puede desactivarse por configuración.

Imagen de ejecución de tests
Un cambio en el código dispara la ejecución de los tests asociados al archivo modificado.

Librerías conocidas de Quarkus

Como ya mencionamos previamente, Quarkus ofrece un gran número de librerías populares ya preparadas para la compilación nativa que incluye, por ejemplo, Jackson, Hibernate, RESTEasy, OpenTelemetry, Apache Camel, entre muchas otras. Esto acelera el desarrollo ya que los programadores pueden trabajar con librerías que ya han utilizado en el pasado.

Dev Services con Quarkus

Al igual que en el caso del live reload de las aplicaciones, el mismo enfoque se implementa para las dependencias externas. Es muy probable que el desarrollo que estemos realizando implique comunicarse con una base de datos, o enviar o recibir mensajes por algún broker de mensajería (Kafka, RabbitMQ, etc.). 

Pues bien, Quarkus provee una funcionalidad a la que llama Dev Services, la cual, normalmente a través de testcontainers, se ocupa de iniciar los servicios necesarios. Por ejemplo, si incluyéramos las dependencias de Hibernate y de Postgres, al iniciar nuestro servicio, Quarkus se encargará de bajar la imagen docker de Postgres e iniciar el contenedor. 

Por supuesto, la imagen a descargar y el comando con el cual iniciar el contenedor son configurables.

Implementación de estándares en Quarkus

Tan importante como la DX, y bastante más desde la perspectiva del equipo responsable de IT de una compañía, es la popularidad y la perspectiva de futuro de las herramientas a utilizar. El hecho de que Quarkus implemente estándares como MicroProfile/Jakarta, CDI, JAX-RS, JPA, OpenAPI, etc. es una gran respuesta en contra de una objeción con respecto a por qué no utilizar algún producto con más presencia del mercado.

Esta adopción de estándares permite que, en gran medida, las aplicaciones construidas con Quarkus sean portables a otros frameworks sin incurrir en enormes costos, debido a que una gran porción del código debiera funcionar sin cambios en otra plataforma que implemente los mismos estándares.

Generación de artefactos para Kubernetes

Mediante una extensión para Kubernetes, Quarkus puede generar los recursos necesarios (para vanilla K8s, para OpenShift y para KNative), construir imágenes de contenedores, subirlas a un registry e incluso desplegarlas en un cluster.

Programación reactiva con Quarkus

En arquitecturas en la nube las aplicaciones comparten los recursos (CPU, memoria, etc.), por lo que, lograr que las mismas sean eficientes es un factor fundamental. Los sistemas reactivos cumplen con una serie de principios (definidos en el denominado Reactive Manifesto) que conforman la base de las buenas prácticas a la hora de construir sistemas distribuidos. Siguiendo estos principios se pueden crear servicios que hagan un uso eficiente de los recursos.

Quarkus está construido con el concepto de reactividad desde el inicio. La arquitectura reactiva de Quarkus se apalanca sobre Eclipse Vert.x (que a su vez utiliza Netty) para administrar operaciones de entrada/salida (I/O) de manera no bloqueante.

La utilización de Vert.x se puede realizar a través de la librería Mutiny o de Kotlin Coroutines. Mediante una combinación de tipos de datos y de indicaciones en forma de annotations, Quarkus se encarga de despachar las operaciones entre los hilos de Vert.x para operaciones no bloqueantes y entre hilos para operaciones bloqueantes (denominados worker threads).

@Path("/people")
public class PeopleController {

	private final PersonService personService;

	public PeopleController(final PersonService personService) {
    	this.personService = personService;
	}

	@POST
	@Produces({MediaType.APPLICATION_JSON})
	@Consumes({MediaType.APPLICATION_JSON})
	@ReactiveTransactional
	public Uni<PersonControllerModel> create(
        	@Valid final UpsertPersonControllerModel request
	) {
    	Log.infov("Call to POST /people with request {0}", request);
    	return personService.create(request.toDomain()).map(person -> {
        	final var response = PersonControllerModel.from(person);
        	Log.infov("Response to POST /people: {0}", response);
        	return response;
    	});
	}

}

Ejemplo de un endpoint reactivo con Quarkus (nótese que el método create responde con un tipo paramétrico Uni de la librería Mutiny).

Conclusión

La evolución del software hacia el concepto cloud native exige replantearse si las herramientas y tecnologías que utilizamos actualmente en el desarrollo de software siguen siendo vigentes. 

Quarkus es un framework moderno, que pretende acercar el desarrollo cloud native a un gran número de programadores Java, buscando preservar lo más posible el ecosistema conocido por ellos, a la vez que promueve estándares y buenas prácticas y facilita la experiencia de desarrollo.

Contacto

Contactanos

¿Cómo podemos ayudarte?