Los «Micro» Frameworks Java más populares

Con el auge de los microservicios y el modelo de facturación de los servicios en cloud, nos vemos en la necesidad de analizar los entornos en los que se ejecutan nuestas aplicaciones. Vamos a analizar los «Micro» Frameworks Java más populares para el desarrollo de capas de servicios.

Tradicionalmente las aplicaciones web Java han sido desplegadas con su correspondiente servidor de aplicaciones, unas veces un Tomcat, un JBoss (Wildfly), un Glassfish (Payara) o cualquier otro contenedor. Estos contenedores aportaban la funcionalidad necesaria para el acceso desde la red a dichas aplicaciones.

Desgraciadamente, con esos servidores de aplicaciones vienen asociados diversos inconvenientes, principalmente que no siempre es transparente llevar nuestras aplicaciones de un contenedor a otro, sea porque la forma de configurarlos es diferente o bien porque generan errores de dependencias compartidas con nuestras propias aplicaciones. Por alguna razón, los distintos contenedores web han ido adentrándose en el terreno de forzar a que las aplicaciones que corren en ellos queden atadas a su implementación.

Con el auge del mundo de los microservicios todos esos contenedores han encontrado un nuevo «problema» asociado, cuando nuestra funcionalidad es pequeña, que es lo esperado en un microservicio, el contenedor se convierte más en una carga de memoria y CPU en lugar de una ayuda. No parece buena idea un pequeño servicio que realice una pequeña función por ejemplo de tratamiento de imágenes se vea sobrecargado por un contenedor de aplicaciones que consuma mucha más memoria que la funcionalidad misma.

Consumo de memoria en Standby:
Tomcat: Mem: 127Mb
Jboss Wildfly: Mem: 315Mb
Payara Micro: 371Mb

Debido a estos consumos muchas veces innecesarios en el escenario de microservicios, han ido apareciendo soluciones autocontenidas, que permiten que las aplicaciones puedan exponer sus servicios sin la sobrecarga de los contenedores.

Sin desmerecer a los contenedores de aplicaciones que claramente tienen un escenario de uso, vamos a analizar algunas de estas soluciones «micro» orientadas específicamente al mundo de los microservicios mediante un simple servicio de echo:

  1. SparkJava
  2. Javalin
  3. Ninja
  4. Micronaut
  5. Dropwizard
  6. SpringBoot
  7. Rapidoid

1. SparkJava

Website: http://sparkjava.com/
Repositorio github: https://github.com/perwendel/spark
Documentación: 7 / 10
Boot Time: 0.3s
Peticiones/segundo: 13500

El framework Sparkjava es un framework web ligero, tan solo nos da una pequeña y muy ligera capa de abstracción sobre un servidor Jetty, dejando bajo nuestra responsabilidad la decisión sobre el resto de librerías que nuestra aplicación necesite utilizar. Pese a no ser un framework que nos ofrezca grandes funcionalidades, nos asegura que difícilmente tendremos un problema de compatibilidad entre las librerías que usamos y las librerías incluidas en el framework.

Por otra parte, al ser tan liviano requiere cierto procesamiento manual de las peticiones, pareciéndose más a un servlet que a un framework completo.

public class SparkServer {

    private Gson gson=new Gson();
    
    public static final void main(String[] args){
        new SparkServer();
    }
    private SparkServer(){
        port(8080);
        post("/echo","application/json", this::echo);
    }

    private Object echo(Request request, Response response) {
        Message input=gson.fromJson(request.body(), Message.class);
        input.setValue("echo "+input.getValue());
        return gson.toJson(input);
    }
    public void stop(){
        Spark.stop();
    }
}
    

A favor:
– Inicio muy rapido
– Tamaño de compilado muy pequeño
– Comunidad activa

En contra:
– Falta de funcionalidades integradas

2. Javalin

Website: https://javalin.io/
Repositorio github: https://github.com/tipsy/javalin
Documentacion: 7 / 10
Boot Time: 0.3 s
Peticiones/segundo : 15000

Javalin es el primo hermano de SparkJava, de hecho es un fork del mismo proyecto sobre el que se han añadido funcionalidades como control de sesiones, validación, soporte transparente a serialización de objetos json, al mismo tiempo que se ha reducido la cantidad de código del proyecto original.

public class JavalinService {
    private Javalin app;
    public static final void main(String[] args){
        new JavalinService();
    }
    private JavalinService(){
         app = Javalin.create().start(8080);
        app.post("/echo", this::echo);
    }

    private void echo(Context ctx) {
        Message item = ctx.bodyAsClass(Message.class);
        item.setValue("echo "+item.getValue());
        ctx.json(item);
    }
    public void stop() {
        app.stop();
    }
}

A favor:
– Inicio muy rapido
– Tamaño de compilado muy pequeño
– Gran velocidad de procesado de peticiones

En contra:
– Falta de funcionalidades integradas
– Poca comunidad

3. Ninja

Website: https://www.ninjaframework.org
Repositorio github: https://github.com/ninjaframework/ninja
Documentación: 4 / 10
Boot Time: 1s
Peticiones/segundo: 10000

Ninja es un framework integrado, es decir, no incluye únicamente la capa de servicios sino también vistas, jpa, o inyección de dependencias. Desgraciadamente está poco documentado y su guía de usuario nos invita a crear un proyecto a partir de un arquetipo maven, lo cual siempre es una señal de alarma.

Por otra parte, la estructura del proyecto generado no es estándar, generándose ficheros de recursos, configuración y vistas, fuera del directorio maven estándar, lo cual si estamos acostumbrados a cierto orden puede resultar molesto.

public class Routes implements ApplicationRoutes {
    @Override
    public void init(Router router) {       router.POST().route("/echo").with(ApplicationController::echo);
    }

}
@Singleton
public class ApplicationController {
    public static Result echo(Message item) {
        item.setValue("echo "+item.getValue());
        return Results.json().render(item);
    }
}

A favor:
– Modo SuperDev para hacer rápidos despliegues durante el desarrollo

En contra:
– Lentitud en el procesamiento de peticiones
– Estructura no estándar de proyecto
– Ofrece un ecosistema muy cerrado, con dificultades para migrarlo

4. Micronaut

Website: https://micronaut.io/
Repositorio Github: https://github.com/micronaut-projects/micronaut-core
Documentación: 8 / 10
Boot time: 1.3s
Peticiones/segundo: 13000

Micronaut es un framework integrado para microservicios que busca principalmente la facilidad de implementar tests y la clara orientación a servicios cloud, dando soporte a gestión de errores y reintentos.

Por otra parte, hemos encontrado algunas dificultades a la hora de probarlo, arrojándonos errores poco claros y no excesivamente documentados debidos al procesamiento de las anotaciones.

@Controller("/")
public class EchoController {
    @Post(uri = "/echo", produces = MediaType.APPLICATION_JSON, consumes = MediaType.APPLICATION_JSON)
    public Message echo(Message message) {
        message.setValue("echo "+message.getValue());
        return message;
    }
}

A favor:
– Alto rendimiento en el procesado de peticiones
– Facilidad de configuración

En contra:
– Es un framework relativamente pesado

5. Dropwizard

Website: https://www.dropwizard.io
Repositorio Github
: https://github.com/dropwizard/dropwizard
Documentación: 7 / 10
Boot time: 1.5s
Peticiones/segundo : 11000

Dropwizard es un framework integrado con Jetty/Jersey como centro de su capa de servicios y otras librerías de utilidad no acopladas y modulares, como JDBI, Guava o FreeMarker

@Path("/")
public class MessageController {
    @POST @Path("/echo") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
    public Response echo(Message message) throws IOException {
        message.setValue("echo "+message.getValue());
        return Response.ok(message).build();
    }
}

A favor:
– Muy ligero en consumo de memoria
– Framework bien documenado
– Las librerías que lo componen son estándar (Jersey, JDBI)
– Desarrollo muy activo

En contra:
– No es muy sencillo implementar la seguridad, existe soporte para autenticación pero no para autorización

6. SpringBoot

Website: https://spring.io/projects/spring-boot
Repositorio github:
https://github.com/spring-projects/spring-boot
Documentación: 10 / 10
Boot time: 2s
Peticiones/segundo: 15000

SpringBoot es el framework más usado en el entorno empresarial, ofrece entre otros cientos de módulos que van desde desde la capa de presentación al acceso a base de datos pasando por el procesamiento distribuido. SpringBoot se beneficia lógicamente de todo lo desarrollado desde los orígenes de Spring, esto es, la inyección de dependencia, y las facilidades de integración con productos de terceros.

Por otra parte, igual que Spring Framework requería de muchos recursos de memoria y CPU, igual le sucede a SpringBoot

A favor:
– Muy documentado
– Ampliamente adoptado por la comunidad
– Multiples librerías igualmente documentadas
– Incluye todas las ventajas del framework Spring
– Buen soporte para seguridad, tanto autenticación como autorización

En contra:
– Compilados bastante grandes
– Lentitud en el arranque
– Alto consumo de memoria

@RestController
@EnableAutoConfiguration
@ComponentScan
public class TextApplication {

    public static void main(String[] args) {
        SpringApplication.run(TextApplication.class);
    }
    @PostMapping(value = "/echo",consumes = MediaType.APPLICATION_JSON_VALUE,produces = MediaType.APPLICATION_JSON_VALUE)
    public Message handle(Message message) {
        message.setValue("output");
        return message;
    }

}

7. Rapidoid

Website: https://www.rapidoid.org/
Repositorio Github: https://github.com/rapidoid/rapidoid
Documentación: 5 / 10
Boot time: 0.9 s
Peticiones/segundo: 17000

Rapidoid es casi un desconocido, es un framework que ofrece tanto una capa rest como un motor de plantillas para la capa de presentación. Su principal ventaja es la velocidad de procesado de las peticiones, siendo la más alta de todas las analizadas.

A favor:
– Altá velocidad de procesado de peticiones, con mínimo impacto en memoria
– Tamaño mínimo de despliegue
– Mínima cantidad de código fuente

En contra:
– Uso casi exclusivo de JSON
– Consumo de memoria en standby
– Poco documentado
– Pocas funcionalidades incluidas
– Poco movimiento en su repositorio

public class Rapidoid {
    public static void main(String[] args){
        On.post("/echo").json((Message message)->{message.setValue("echo "+message.getValue());return message;});
    }
}

Resumen:

Frameworks Java
Peticiones / Segundo , 50000 requests , 200 concurrentes
Frameworks Java
Tiempo de inicio de las aplicación (Standalone)
Frameworks Java
Tamaño en Mb de la aplicación compilada como .jar, con las dependencias por defecto
Frameworks Java
Memoria en Mb utilizado por las aplicaciones en el estado inicial, procesando peticiones y finalizado el procesado

Conclusión

A la hora de elegir un framework para desplegar como microservicios es necesario tener muchos factores en cuenta, la memoria es un factor tan importante como la velocidad de proceso, ya que ésta normalmente afecta profudamente en el coste de los servidores. De forma general será más fácil adaptar código existente a microservicios apoyándonos en frameworks más completos, como SpringBoot o Dropwizard, mientras que si estamos diseñando un sistema desde cero, diseñando todo en base a microservicios, nos será más cómodo tomar como base un framework ligero, como Spark, (o si nos sentimos muy valientes como Rapidoid) e ir añadiendo nuestras dependencias sobre ellos.