Quarkus had its first production-ready release in November 2019 and quickly became huge news in the Java community. Why? It fills a big gap in the Java ecosystem focusing on the Kubernetes platform, empowering developers to build and deploy small, optimized applications that can run either as standalone services or as serverless functions. Implementing Java applications with low memory usage and fast boot time is no longer only a dream. Quarkus uses GraalVM and builds ahead of time (AOT) to package a OS-native artifact that can be deployed without using the Java Virtual Machine. Let’s understand how it works!
Problem: Cloud Native Apps and Serverless
The past few years have shown a change in the way we deliver software. Requirements are constantly changing to meet growing business needs. Cloud computing has become the default approach for building million-users applications and the different technology ecosystems have been working hard to create solutions to the incoming problems that those new approaches are bringing us.
Cloud native applications are built to scale. They feature the ability to process huge volumes of data with low latency with high availability – just what you need to deliver the best user experience.
A lot of cloud providers and technology communities are working to provide tooling, pattern and practices that enable software developers to build applications that meet those requirements with the lowest cost possible.
The Existing Solutions in the Java World
The Java platform has been the leading technology for enterprise software for a couple decades now, and always has been on top regarding the needs of the software development community. Looking at the existing Java solutions to build microservices and cloud native applications, we can see examples like Spring Boot with Spring Cloud, Wildfly Tornthail and Dropwizard. These tools represent the first class of the Java solutions for cloud applications, but they have become insufficient.
The main problem here is related to the memory usage and the boot time of the application. See, if the application is being built for the cloud, memory is directly related to the financial cost, and the time the application takes to be ready impacts directly the scalability and resilience of the environment, since it might need to boot new instances of the same service in case a fail happens or there is a growth of incoming requests.
In the beginning of Java’s history the requirements that were considered to build the language were mainly to ensure the same Java code ran in different operational systems, mainly when building Java desktop softwares – the solution then was to create a virtual machine. For the Java world these days, running the same application in different environments is something unnecessary, since when deploying Java to the cloud, the developer has control of the environment and its operational system.
Quarkus as a Solution
Image from the developers.redhat.com website.
In that scenario, the Java community has launched Quarkus, which is presented as a solution to build Java applications deployables to Kubernetes.
Quarkus has a close relation to the GraalVM, allowing developers to build Java applications and build a native artifact targeting the operational system, such as Linux. Instead of building a Jar artifact that would run on-spot on the JVM, GraalVM builds this native artifact that handles everything that a Java application needs to run, including the Java Platform default modules. With the native artifact, it can be easily deployed as a docker container inside a Kubernetes cluster. Quarkus also applies strategies to build the code AOT – Ahead Of Time, meaning those techniques that usually are done at runtime. Quarkus build time AOT allows for Dependency Injection and dead-code removal.
So far the metrics collected by the Quarkus community about memory usage and time for the first request has been more than satisfactory. As you can see down below, when compared with a regular cloud stack, Quarkus uses around 28 MB of memory and boots in 0.042 seconds against 209 MB and 9.48 seconds for the regular cloud stack.
Image from the Quarkus.io website.
Extensions
Quarkus also allows the engineers to use the commonly known frameworks and tools that already exist in the Java ecosystem. It does that by using a concept of extensions, that are implementation of existing frameworks using techniques that replace reflection and make it more adaptable to the Quarkus needs, making sure the extension will not impact negatively on the main requirements regarding memory and boot time.
Thanks to the community’s hard work, Quarkus already has a lot of extensions, for example CDI – Context and Dependency Injection, JPA – Java Persistence API, JAX-RS, Microprofile, NoSQL DBs clients, Vert.X for reactive programming, Kubernetes client and manifests generating. Besides Java, Quarkus services can also be written using Kotlin.
While migrating towards Quarkus, developers that are used to working on tools already using other Java application frameworks, the coding difference is minimal. The main extensions follow the Jakarta EE specifications, which means that legacy codes from older Java applications would work on Quarkus and its compatible extensions without any major change. Even Spring standard code can also be migrated since there are available extensions for the main Spring ecosystem modules, like DI, Web and Data.
Developer Joy
One other important thing to mention is that the Quarkus team is very concerned about how the developers will feel while developing their new services. A lot of features were developed to make the experience of using Quarkus even better.
Its development mode comes with an instantly hot reload that allows us to see the change right after re-requesting the API that we’re working on. For those that are used to other Java frameworks that require minutes of waiting Maven build working to see the results, the Quarkus hot reload is an amazing feature.
Image from the Quarkus.io website.
By using a Maven/Gradle plugin on the command line, the developer can create a service with the boilerplate code already done, easily list available extensions and add them to the project. Since Quarkus is made for Kubernetes deployment, once the project is created using the CLI plugin, a Dockerfile is already created ready to be used to create new docker containers running the JVM or the native mode. After adding the Kubernetes extension to the project, while packaging the code, it will also generate the Kubernetes manifests to deployment and service, using the metadata defined on the project.
Example of Maven command line to create a new project
mvn io.quarkus:quarkus-maven-plugin:1.4.1.Final:create \ -DprojectGroupId=org.acme \ -DprojectArtifactId=getting-started \ -DclassName="org.acme.getting.started.GreetingResource" \ -Dpath="/hello"
Example of Maven command line to list the available extensions
./mvnw quarkus:list-extensions
Example of Maven command line to add a new extension
./mvnw quarkus:add-extension -Dextensions="hibernate-validator"
Conclusion
Quarkus enables developers to build even more reliable applications that can be offered to millions of users with performance and resilience. Its close relationship with Kubernetes brings the Java ecosystem to the stage to compete for building cloud native and serverless applications. There is a lot of Java code running out there, and now those applications can be migrated to a modern Kubernetes cluster, that makes the developers and operations work easier and trustful.
For the next few years, we’ll be able to see how different Java applications will be implemented, considering the new possibilities, and the positive impact the Quarkus will have on that change.