Micronaut

Over the last decade or so, we’ve seen our Java deployments get smaller and smaller. My journey looks something like this:

  1. Big, heavyweight Java EE application servers like IBM WebSphere (this got too slow).
  2. Switch to something more lightweight, like WildFly (this got slow too).
  3. Migrate to a Tomcat/Jetty/Netty + Spring contraption (this got slow too).
  4. Put it into Docker containers (this got slow too).
  5. Move to Spring Boot, a standardized contraption (this got slow too).

Notice the trend? At every step, things ultimately got slow. The reason is that our solutions got larger, while our deployment units got smaller. The result is roaring overhead (CPU/memory/build time). I believe this is because we’re using the tools in a way they weren’t designed for; WildFly wasn’t designed to be used in a microservice environment.

Since we’re in a microservice environment we’d like the functionality of a framework like Spring Boot, but faster and more lightweight (lower memory usage, smaller Docker images, faster startup, etc). Micronaut promises to here, so I gave it a try.

Migrating from Spring Boot to Micronaut

The application I migrated is a typical microservice; persistence in MongoDB, communication over gRPC and monitoring using Micrometer/Prometheus.

Since Micronaut took quite a few principles from Spring Boot, migration is pretty easy. Roughly, the process looks something like this:

  1. Add Micronaut’s core Maven dependencies.
  2. Find out which Spring Boot Starters you use, and find the corresponding Micronaut Project.
  3. Fix compilation errors by replacing imports (Micronaut generally uses the same annotation names as Spring).
  4. Fix your application.yaml configuration file.
  5. Run it.

However, there are a few gotchas:

  • Spring’s default bean scope is Singleton while Micronaut’s default bean scope is Prototype. If you just remove @Component/@Service/@Repository/etc and let Micronaut do its magic, you might unintentionally end up with multiple instances of your bean. If that’s a problem, remember to mark your beans with @Singleton (javax.inject.Singleton) to mimic Spring’s behaviour.
  • Spring Boot supports exposing the management interface on a different port (than your web application) for extra security. This is not supported by Micronaut.

The Result

My observations:

  • Boot time: Much faster. I expected Micronaut to be faster (since that’s what is advertised), but I didn’t expect it to be that much faster.
  • The memory usage right after booting is a bit lower. For more realistic numbers that should be measured after some actual application usage.
  • Compiling a Micronaut application is quite a bit slower than a Spring Boot application. However, if you look at the actual numbers, that might not really be a problem since it’s just 7 seconds. I found out that quite a lot of time (~5 seconds) is taken by the maven-shade-plugin, which seems to be a lot slower compared to its Spring Boot counterpart (spring-boot-maven-plugin). That’s something that can be optimized, probably.

The actual numbers:

  Spring Boot Micronaut
Startup time: 24 seconds 4 seconds (600% faster )
Memory usage: 190 mb 160 mb (18% smaller)
Compilation time: 9 seconds 16 seconds (77% slower)

Is it worth it?

It can be worth it. Especially if Spring Boot is booting too slow causing actual problems (e.g. causing Kubernetes readiness/liveness time-outs or it makes your development cycle unacceptably slow) then it’s definitely an option.

Ultimately it’s a trade-off: Your applications will boot much faster, will compile a bit slower, (probably) consume a little less memory but you loose Spring’s feature-rich ecosystem (although it’s possible to use Spring libraries without the Spring container).