It seems like in technology some things happen at "the speed of light". I feel like almost every day we have new OS updates, programming libraries, programming languages, frameworks, databases. We sail through an endless ocean that has vastly changed and re-shaped from 10, 15, 20 years ago.

Some people like to become expert 100% in one domain; say a certain programming language, or machine learning, databases, cloud architecture, security information, etc. Others try to cover as broad as possible. In my case I would say in both sides I feel excited and comfortable. This summer I've been interested in the Java VM and more concretely in the GraalVM.

GraalVM is a high-performance runtime that provides significant improvements in application performance and efficiency which is ideal for microservices. The first production-ready version, GraalVM 19.0, was released in May 2019. The project aims to increase application throughput and reduce latency, compile applications into small self-contained native binaries and seamlessly use multiple languages and libraries: Run Programs Faster Anywhere.

Key Features

High Performance: advanced optimizing compiler that generates fast lean code which requires fewer compute resources.

Ahead-of-Time Compilation: native binaries start up instantly and deliver peak performance with no warm up time.


In a Serverless world (present and future) we think in terms of lighting-quick startup times, low-memory utilisation. A code/function is loaded  on demand and needs to start fast.  The delay comes when your selected runtime container is provisioned then running your function. Here is where Java makes you cry and scripting languages catch your attention.

The truth is GraalVM enabled a new approach to tackle this scenario from a Java Stack perspective. Here is where Quarkus and Micronaut came to play and push Java into the Serverless space. Boasting fast startup times and low memory consumption.

These two Java frameworks tailored for GraalVM level up Java in a microservices environment. It's a game changer.

There are a bunch of performance benchmark to take a look. To name one: Quarkus VS Spring Boot.

Start-up time 🔥

Quarkus < 0.010 ms
Spring > 6.500 ms

Memory Footprint 😱

Quarkus < 2.1 MB
Spring > 148 MB

At least interesting to play a bit around, huh?

I've been working recently in a project based on Quarkus, exploring and experimenting its features and power. I built a reactive and event-driven REST API. As use case I chose a CRUD of a backend session with a reduced business domain: a task/todo list. You can find the code here.

Hands on! 🛠


Dependencies


One good thing of Quarkus is that it is built on the top of the best of breed Java libraries and standards. The project includes the dependencies for implementing the REST API based on Vertx and Reactive libraries as Smallrye. Also it uses Microprofile: a specification and solid option to build your next-generation microservices architecture with enterprise Java. It also included some additional dependencies (not required for our propose).
The project uses Mutiny which is a reactive programming library allowing to express and compose asynchronous actions. It offers 2 types:

  • io.smallrye.mutiny.Uni - for asynchronous action providing 0 or 1 result
  • io.smallrye.mutiny.Multi - for multi-item (with back-pressure) streams

Routing

With Quarkus you can leverage JAX-RS to create an REST API or you can use Vertx Reactive Routes, which is the option in the code above. A service to process the request is injected.  

Processing

A session service is responsible to map the request to model domain and store it on DynamoDB, using an injected Database service.
You should notice the annotation @Dependent over the service. This allow you declare the context and scope of a Bean.
Quarkus DI solution is based on the Contexts and Dependency Injection for Java 2.0 specification.

Storing on Database

The project uses DynamoDB for storing sessions. The table is called supersonic-sessions. The structure of the table uses session_id as Hash Key. There are several ways to create the table. Below the command line option:

If you explore the project, you will notice that for local testing it is using for DynamoDB a docker container and it creates this table as part of the docker-compose.yml.

The Database service uses a mapper to separate the domain object (Session) from the domain DB item stored. This is for abstract complexity between the business and database model. Also a save expression is defined in order to avoid override an existent item given a key (session_id)

What about Events?


Once this service runs, a bunch of different events will happen. Let's take a create session event as an example to demonstrate how to process and propagate it. In this architecture, other services might be interested about this event. A new session might trigger an email, a product recommendation, a risk scoring algorithm, and so on.

In an event-driven world, Kafka shines. This project integrated Kafka as an event backbone to decoupled business and responsibilities from stakeholders and allow them to react to changes in the domain.

The services leverage in the Vertx Event-Bus to send and process messages asynchronous. The following service is in charge of this as a producer role.

The consumer will process every event and perform all the asynchronous tasks: logging, publish metrics, send to Kafka, send to AWS S3 Bucket, etc, etc.

For the sake of explanation the service itself is the same who will consume from Kafka (in real life, another service would be the consumer). Microprofile allows us to define Outcoming/Incoming methods in a declarative way. You only need to write the logic for producing and consuming and annotate the method (in/out) and with a proper static configuration your services will be integrated with a Kafka system. As simple as that.

Run locally

How to run SUSE locally

  • Spin up dependencies: make docker.run.dependencies: Kafka + DynamoDB
  • Run PASS ./gradlew quarkusDev
  • The folder postman includes the CRUD requests to test.

The default profile is dev

Build Native

  • Set quarkus.package.type:native in application configuration
  • Build the application: ./gradlew quarkusBuild
  • Grab a coffee and wait :)
  • Build the docker image: docker build -f src/main/docker/Dockerfile.native -t pass/pass .
  • Run the docker container: docker run -i --rm -p 8080:8080 pass/pass

Build (fast) JAR

  • Set quarkus.package.type:fast-jar in application configuration
  • Build the application: ./gradlew quarkusBuild
  • Build the docker image: docker build -f src/main/docker/Dockerfile.jvm -t pass/pass .
  • Run the docker container: docker run -i --rm -p 8080:8080 pass/pass

What's next?

I've created my first application with Quarkus and in addition I've followed a reactive and event-driven approach to build a simple REST API. I will iterate a bit this project with:

  • Performance benchmark.
  • Part 2 of this post with numbers: response time,  memory footprint, container startup time.
  • ETag for optimistic locking support plus concurrency tests

Takeaways

  • AOT added in Java 9 is super interesting and hot topic to level up Java performance.
  • Quarkus is going through a solid path optimizing Java specifically for containers and enabling it to become an effective platform for serverless, cloud, and Kubernetes environments.  
  • Quarkus increase productivity. You don't need to learn from scratch. Java standards, frameworks, and libraries reduce the learning curve.
  • Quarkus helps us to build reliable systems: elastic and scalable.


Conclusion


Time is gold and when it comes the build scalable Java application in a microservices environment being faster and efficient matters: auto scaling (up/down), deployments, coding, stability, reliability.
New frameworks are drastically improving and reshaping this conception. A monolithic application is already far away in the past from the present. Now it's time to explore new options to turn a traditional Java service into something even more robust in terms of performance. Quarkus is a good starting point to experimenting.

References

Ahead of Time Compilation (AoT) | Baeldung
A quick and practical overview of the concept of Ahead of Time Compilation.
Deep Dive Into the New Java JIT Compiler - Graal | Baeldung
Explore the functionalities of the new Java JIT compiler - Graal.
GraalVM Community
GraalVM is a high-performance runtime that provides significant improvements in application performance and efficiency which is ideal for microservices.
Quarkus Runtime Performance
Quarkus: Supersonic Subatomic Java
Microservices: Quarkus vs. Spring Boot - DZone Microservices
In the era of containers (the ‘Docker Age’) Java still keeps alive, being struggling for it or not. Who will win: Spring Boot or Quarkus.
Vert.x Core Manual - Vert.x
Eclipse Vert.x is a tool-kit for building reactive applications on the JVM.
Quarkus - Using Apache Kafka with Reactive Messaging
Quarkus: Supersonic Subatomic Java