Micronaut, a New Contender for the Microservices Era

Introduction

Micronaut is a web framework similar to Spring Boot, which started at OCI and was initially developed by Groovy and Rails developers from Pivotal.

Micranaut is really a competitor for Spring Boot, and if you are comfortable with Spring Boot, Micronaut provides a Spring-like way of creating web applications. It uses JAX-RS annotations and specific Micronaut annotations similar to the Spring Boot ones (sometimes the only difference is the package name (io.micronaut)).

Getting Started

A CLI tool similar to the one provided by Spring is avaialable to create your Micronaut applications. The simplest way to start using the CLI tool is to install it through SDKMAM.

curl -s https://get.sdkman.io | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk install micronaut
mn --version

Now you can create your first Micronaut project.

mn create-app com.sergiomartinrubio.micronaut-example --build maven

By default, it uses Gradle build system and Java as the main language, however, you can tweat these setting by using these two flags: --build [gradle, manven] and --lang [java, groovy, kotlin]. You can also add features to the project, like GraalVM support, Zipkin for tracing, type of rendering…

GraalVM: it is an ecosystem which supports many languages and allows you to run your applications like a native one, so in the case of Java, it will translate your bytecode into machine code.

When nm command is run, it esentially creates a base folder with the build system file chosen (if you choose Gradle, it also provides a Gradle wrapper, so it downloads it for you), a Dockerfile ready to run, and a logback file for log configuration.

Features

You can crate controllers by using @Controller annotation and define your endpoints with http method annotations like @Get. When you run the application, you can hit the endpoint at http://localhost:8080, so it will feel as a Spring Boot application.

@Controller
public class UpperCaseController {

    @Get("/touppercase/{input}")
    public String toUpperCase(@PathVariable("input") String input) {
        return input.toUpperCase();
    }
}

Tests are also created in a similar way as the Spring Boot way. Instead of @SpringBootTest annotation, it uses @MicronautTest, and RxHttpClient annotated with @Client instead of autowiring TestRestTemplate, to make HTTP requests to your controller.

@MicronautTest
class UpperCaseControllerTest {

    @Inject
    @Client("/")
    RxHttpClient client;

    @Test
    public void toUpperCase_givenStringPathParam_whenParamIsValid_thenReturnsStringInUpperCase() {
        HttpRequest request = HttpRequest.GET("/touppercase/test");
        String response = client.toBlocking().retrieve(request);

        assertNotNull(response);
        assertEquals("TEST", response);
    }
}

Source Code

Micronaut provides a compile-time AOP API that does not use reflection. For those who don’t know what reflection is, it is basically a way to the program to introspect itself, so it asks itself if a class has specific methods, annotations, parameters… and Spring uses intensively reflection to search for controllers, endpoints, beans… so there is a performance penalty that you pay, and most of this work is done during startup. Therefore, Micronaut has a great advantage in the serverless model, where startup times are very important.

Micronaut relies on annotations, it uses these annotations in a different way as Spring. During compile-time, it scans all the different annotations and add extra code to hook everything up. This means that once the Java jar is compiled, annotations are not needed anymore, so there is no need to do any reflection, because the code is already baked into the jar. Bear in mind that Micranaut doesn’t modify the original classes, instead it creates extra classes, so if you need to debug your code, it will stop on the line you are expecting to stop.

Micronaut Additional Classes
Micronaut Additional Classes

Micranaut tries to make it compatible with GraalVM as much as possible, and even has a feature to generate a project with Graal support. This is done by creating a Docker image with GraalVM, JDK and the Java binaries.

Micronaut only supports Netty and there is no way at the moment to switch to Tomcat, so you have to considerer if Spring metrics and threading handling of Tomcat is a must. However, it is compatible with Micrometer, provides refresh and healthcheck endpoints, cache endpoints….

Other features:

Mironaut vs Spring Boot performance

Three metrics will be measure to decide which framework performs better. Both, Spring Boot and Micronaut applications contain same features: http web support with only one class where a controller is defined, and a single endpoint, which converts a string to upper case. Spring uses Tomcat as the embedded web server, whereas the Micronaut one uses Netty.

Startup Time

docker run -p 8090:8090 spring-boot-example

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.4.RELEASE)

2019-04-30 11:18:47.729  INFO 1 --- [           main] c.s.s.SpringBootExampleApplication       : Starting SpringBootExampleApplication v0.0.1-SNAPSHOT on 960c5b1f8f80 with PID 1 (/spring-boot-example.jar started by root in /)
2019-04-30 11:18:47.733  INFO 1 --- [           main] c.s.s.SpringBootExampleApplication       : No active profile set, falling back to default profiles: default
2019-04-30 11:18:49.503  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8090 (http)
2019-04-30 11:18:49.546  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-04-30 11:18:49.546  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.17]
2019-04-30 11:18:49.634  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-04-30 11:18:49.635  INFO 1 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1828 ms
2019-04-30 11:18:49.974  INFO 1 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-04-30 11:18:50.247  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8090 (http) with context path ''
2019-04-30 11:18:50.252  INFO 1 --- [           main] c.s.s.SpringBootExampleApplication       : Started SpringBootExampleApplication in 3.001 seconds (JVM running for 4.299)
2019-04-30 11:21:49.215  INFO 1 --- [nio-8090-exec-7] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-04-30 11:21:49.216  INFO 1 --- [nio-8090-exec-7] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2019-04-30 11:21:49.247  INFO 1 --- [nio-8090-exec-7] o.s.web.servlet.DispatcherServlet        : Completed initialization in 30 ms

It takes ~3 seconds to start the Spring Boot application.

docker run -p 8080:8080 micronaut-example  
11:19:08.888 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 1352ms. Server Running: http://bca092ae4eb6:8080

In only ~1.3 seconds the Micranaut app is up and running.

Spring Boot startup is more than 50% slower.

Winner: Micronaut

Initial Memory Consumption

docker ps

CONTAINER ID        IMAGE                 COMMAND                  CREATED              STATUS              PORTS
                             NAMES
b883c35548e1        spring-boot-example   "/bin/sh -c 'java -X…"   About a minute ago   Up About a minute   8080/t
cp, 0.0.0.0:8090->8090/tcp   tender_booth
ba1be364bd13        micronaut-example     "/bin/sh -c 'java -X…"   About a minute ago   Up About a minute   0.0.0.
0:8080->8080/tcp             crazy_blackwell
docker stats

CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
ba1be364bd13        crazy_blackwell     0.11%               47.36MiB / 7.676GiB   0.60%               7.99kB / 168B       34.9MB / 4.1kB      29
b883c35548e1        tender_booth        0.27%               70.78MiB / 7.676GiB   0.90%               5.87kB / 0B         20.4MB / 4.1kB      45

where b883c35548e1 is the Spring Boot app and ba1be364bd13 is Micronaut.

Micronaut consumes around 30% less memory than Spring Boot.

Winner: Micronaut

Heavy Load Memory Consumption

CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
ba1be364bd13        crazy_blackwell     23.80%              339.1MiB / 7.676GiB   4.31%               3.24MB / 2.45MB     38MB / 4.1kB        52
b883c35548e1        tender_booth        22.52%              123.4MiB / 7.676GiB   1.57%               3.23MB / 2.35MB     21.9MB / 4.1kB      170

Surprisingly, under heavy load Micronaut performs much worese in terms of memory consumption. Micronaut uses 65% more memory than Spring under the same conditions.

Winner: Spring Boot

Response Time

Micronaut Request	9994	2	2	4	5	9	0	50	0.0	97.26710008953945	12.823299328210767	12.443349718486004
Spring Boot Request	9994	2	2	3	4	8	0	63	0.0	97.27467393420285	11.114391455372786	12.444318638066965

Both frameworks perform in the same way.

Winner: tie

Conclusion

Micronaut startup time and initial memory consumption are much better than Spring Boot ones, however, the high memory consumption of Micronaut during heavy load is quite concerning and it seems memory management is not that great.

When should I use Micronaut?

Micronaut team advertise it as a low memory consumption framework with a quick startup time, so it is very suitable for the serverless world.

This framework is also a good choice if you are moving your application to the cloud or you are creating a project from scratch.

Caveats