The most popular “Micro” Java Frameworks

With the rise of microservices and the billing model of cloud services, we are in need of analyzing the environments in which our applications run. We will analyze the most popular “Micro” Frameworks Java for the development of service layers.

Traditionally Java web applications have been deployed with their corresponding application server, sometimes a Tomcat, a JBoss (Wildfly), a Glassfish (Payara) or any other container. These containers provided the necessary functionality to access these applications from the network.

Unfortunately, various inconveniences are associated with these application servers, mainly which it is not always transparent to take our applications from one container to another, either because the way to configure them is different or because they generate dependency errors shared with our own applications. For some reason, the different web containers have been entering the field to force the applications that run in them to be tied to their implementation.

With the rise of the microservices world all these containers have found a new associated “problem”, when our functionality is small, which is expected in a microservice, the container becomes more of a memory and CPU load instead of a help. It does not seem a good idea a small service that performs a small function for example image processing is overloaded by an application container that consumes much more memory than the functionality itself.

Memory consumption in Standby:

Tomcat: Mem: 127Mb
Jboss Wildfly: Mem: 315Mb
Payara Micro: 371Mb

Due to these often unnecessary consumptions in the microservices scenario, self-contained solutions have been appearing, which allow applications to expose their services without container overload.

Without detracting from the application containers that clearly have a usage scenario, we are going to analyze some of these “micro” solutions specifically oriented to the world of microservices through a simple echo service:

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

1. SparkJava

Website: http://sparkjava.com/
Github repository: https://github.com/perwendel/spark
Documentation: 7/10
Boot Time: 0.3s
Requests/second: 13500

The Sparkjava framework is a lightweight web framework, it only gives us a small and very light layer of abstraction on a Jetty server, leaving the decision on the rest of libraries that our application needs to use under our responsibility. Despite not being a framework that offers us great functionalities, it assures us that we will hardly have a compatibility problem between the libraries we use and the libraries included in the framework.

On the other hand, being so light requires some manual processing of requests, resembling a servlet rather than a complete framework.

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();
    }
}

In favor:
• Start very fast
• Compilation size too small
• Active community


Against:
• Lack of integrated functionalities

2. Javalin

Website: https://javalin.io/
Github Repository: https://github.com/tipsy/javalin
Documentation: 7/10
Boot Time: 0.3 s
Petitions/second: 15000

Javalin is the first cousin of SparkJava; in fact it is a fork of the same project on which functionalities such as session control, validation, and transparent support for serialization of json objects have been added, while at the same time it has reduced the quantity of original code of the project.

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();
    }
}

In favor:
• Very fast start
• Compilation size too small
• High speed of request processing


Against:
• Lack of integrated functionalities
• Little community

3. Ninja

Website: https://www.ninjaframework.org
Github Repository: https://github.com/ninjaframework/ninja
Documentation: 4/10
Boot Time: 1s
Requests/second: 10000

Ninja is an integrated framework, that is, it does not only include the services layer but also views, jpa, or dependency injection. Unfortunately it is poorly documented and its user guide invites us to create a project from a maven archetype, which is always an alarm signal.

On the other hand, the structure of the generated project is not standard; generating resource files, configuration and views, outside the standard maven directory, which if we are accustomed to a certain order can be annoying.

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);
    }
}

In favor:
• SuperDev mode to make rapid deployments during development


Against:
• Slow processing of requests
• Non-standard project structure
• It offers a very closed ecosystem, with difficulties to migrate it

4. Micronaut

Website: https://micronaut.io/
Github Repository: https://github.com/micronaut-projects/micronaut-core
Documentation: 8/10
Boot time: 1.3s
Petitions/second: 13000

Micronaut is an integrated framework for microservices that mainly seeks the ease of implementing tests and the clear orientation to cloud services, supporting error and retry management.

On the other hand, we have encountered some difficulties when testing it, generating unclear and not excessively documented errors due to the processing of the annotations.

@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;
    }
}

In favor:
• High performance in the processing of requests
• Easy setup


Against:
• It is a relatively heavy framework

5. Dropwizard

Website: https://www.dropwizard.io
Github Repository: https://github.com/dropwizard/dropwizard
Documentation: 7/10
Boot time: 1.5s
Petitions/second: 11000

Dropwizard is an integrated framework with Jetty/Jersey as the center of its service layer and other unmatched and modular utility libraries, such as JDBI, Guava or 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();
    }
}

In favor:
• Very light in memory consumption
• Well documented framework
• The libraries that compose it are standard (Jersey, JDBI)
• Very active development


Against:
• It is not very easy to implement security, there is support for authentication but not for authorization

6. SpringBoot

Website: https://spring.io/projects/spring-boot
Github repository: https://github.com/spring-projects/spring-boot
Documentation: 10/10
Boot time: 2s
Petitions/second: 15000

SpringBoot is the most used framework in the business environment, it offers among other hundreds of modules that range from the presentation layer to database access through distributed processing. SpringBoot logically benefits from everything developed since the origins of Spring, that is, dependency injection, and the integration facilities with third-party products.

On the other hand, just as the Spring Framework required a lot of memory and CPU resources, the same happens to SpringBoot.

In favor:
• Very documented
• Widely adopted by the community
• Multiple libraries equally documented
• Includes all the advantages of the Spring framework
• Good support for security, both authentication and authorization


Against:
• Pretty large compiles
• Slow start
• High memory consumption

@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/
Github Repository: https://github.com/rapidoid/rapidoid
Documentation: 5/10
Boot time: 0.9 s
Petitions/second: 17000

Rapidoid is almost unknown; it is a framework that offers both a rest layer and a template engine for the presentation layer. Its main advantage is the speed of processing requests, being the highest of all analyzed.

In favor:
• High processing speed of requests, with minimal impact on memory
• Minimum deployment size
• Minimum amount of source code


Against:
• Almost exclusive use of JSON
• Standby memory consumption
• Little documented
• Few features included
• Few movement in your repository

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

Summary:

Petitions/second

Petitions/second, 50,000 requests, 200 concurrent
Application startup time
Application startup time (Standalone)
Size in Mb of the application
Size in Mb of the application compiled as .jar, with the default dependencies
Memory in Mb used by applications in the initial state, processing requests and processing finished
Memory in Mb used by applications in the initial state, processing requests and processing finished

Conclusion

When choosing a framework to deploy as microservices it is necessary to take into account many factors, memory is a factor as important as the speed of process, since this normally affects profusely in the cost of servers. In general, it will be easier to adapt existing code to microservices based on more complete frameworks, such as SpringBoot or Dropwizard, while if we are designing a system from scratch, designing everything based on microservices, it will be more convenient for us to take a light framework as a basis, as Spark, (or if we feel very brave as Rapidoid) and add our dependencies on them.