What is Quarkus?

The programming language Java is known as a long-established “industry language”. It was created in the early years of the Internet, but the web has developed rapidly since then. In addition to classic client-server architecture, there are a number of exciting alternative models: container-based applications, microservices, serverless computing, and reactive web apps have established themselves in the mainstream. These types of applications have been difficult to implement with Java. With the Quarkus framework, that’s about to change. RedHat manager Ken Johnson put it this way:

Quote

“… it’s a very small Java stack, perfect for containers, serverless and other scenarios where you’re running Java applications in the cloud.”

– Ken Johnson, Source: www.openshift.com/blog/quarks-is-here-for-your-java

We will introduce you to Quarkus and show you how this framework is revolutionizing the creation of Java applications.

What makes Quarkus special?

Quarkus is a framework developed by RedHat for creating Java applications. Quarkus was developed with the goal of running Java programs in containers. In particular, it focuses on supporting orchestration software Kubernetes. Another focus of Quarkus development is on the use of established Java libraries and standards.

“HotSpot”, from the OpenJDK project, is used as a Java Virtual Machine (JVM) to be the execution layer for Java code. In addition, the “GraalVM” development, which builds on HotSpot, can also be used. The latter allows Java code to be compiled into directly executable machine code. To understand the immediate benefit of using Quarkus, let’s first look at how Java applications run with and without Quarkus.

How have Java applications traditionally been executed?

The basic idea that made Java revolutionary when first introduced was as simple as it was captivating: Java would make it possible to write a program without being tied to a specific hardware or operating system. Such platform independence is often summed up by the phrase “write once, run anywhere”. The associated portability allows the program to be moved between platforms. What a great trick! So how does it work?

As with other programming languages, a Java program begins with source code that can be read by a human. In order to execute the instructions of the source text on a computer, corresponding instructions are generated in the format of the specific processor. With Java, there is another intermediate step: The source text is first translated into an intermediate format, the so-called bytecode, as is the case with the Python language. The bytecode is then executed in the “Java virtual machine” (JVM). In order to run a Java program on a device, a JVM must be installed on it.

The bytecode is traditionally interpreted for execution in the JVM. The bytecode instructions are translated piece by piece into machine code instructions and executed. The process of “just-in-time compilation” (JIT) is more effective. With that process, the bytecode is also converted into machine code, but further optimizations also come into play. In sum, running a Java program involves the following steps:

  1. Compiling Java source code to bytecode with the Java compiler command ‘javac’:
javac java_program.java
  1. Executing the Java bytecode with the Java runtime command ‘java’ - Machine code is then generated:

 

java java_program
Note

We are talking about a “virtual machine” here. Although the term is the same, in this instance, it does not refer to any kind of technology for virtualizing an operating system. Instead, intermediate code is translated into machine code.

As practical as Java’s “write once, run anywhere” model is, the approach has some weaknesses. Using the JVM entails quite a significant overhead. On the one hand, a certain amount of time is required to start the JVM, which is added to the runtime of the actual app. On the other hand, in addition to higher memory consumption, there is a loss of performance. All of this plays a minor role in applications that run for a long time. However, the approach is not very suitable for short-lived, container-based applications. Ideally, these should start as soon as possible. A start time of several seconds is unacceptable.

How do Java applications run with Quarkus?

In contrast to the native execution of Java applications, Quarkus offers several advantages. Let’s differentiate between the two modes supported by Quarkus:

  1. Optimization of the bytecode and execution in the JVM
  2. Running as native code after compilation

Java code written with Quarkus can be executed normally in the JVM. However, there are considerable advantages in terms of memory consumption and start time of a running application. To achieve this, Quarkus uses a few tricks. In particular, a number of time-consuming steps are moved from the execution to the build process. This includes the steps that otherwise occur every time a Java application is executed:

  • Loading and parsing configurations
  • Scanning the Java class path and resolving annotations
  • Creating entity models for databases or the like where applicable

With Quarkus, these steps are carried out once and the results are cached for quick retrieval. Further performance optimization comes in the form of Quarkus reducing the amount of dynamic information available at runtime. This is replaced by corresponding static constructs. This is particularly useful with regard to use in containers. A containerized application is usually not changed anyway and always runs in the same environment.

The second mode supported by Quarkus for running Java applications is even more interesting. With “ahead-of-time compilation” (AOT), directly executable machine code is generated from the Java source text instead of bytecode, meaning there is no longer any need for a JVM on the target hardware. The program only runs on specific processor architecture and has to be recompiled for other platforms. However, this restriction is usually irrelevant for use in containers. The savings in memory consumption and application startup time achieved with AOT compilation are nothing short of breathtaking. Compare the performance benchmarks shown here from the official Quarkus homepage:

Application Scenario Memory usage Time for first response
Quarkus + AOT REST 12 MB 0.02 s
Quarkus + AOT REST + CRUD 28 MB 0.04 s
Quarkus + JIT REST 73 MB 0.94 s
Quarkus + JIT REST + CRUD 145 MB 2.03 s
Cloud Native Stack REST 136 MB 4.3 s
Cloud Native Stack REST + CRUD 209 MB 9.5 s
Note

Regarding the terminology: REST means that only one web server is running in the container. In the REST + CRUD scenario, a database is running alongside the web server. For cloud native stack, the container contains a JVM, in addition to the Java application.

What is Quarkus used for?

Quarkus is not just another application framework. Instead, the software is meant to redefine what it means to develop applications with Java. As a reminder: traditionally, it was more important for a Java application to run stably for a long time. How long the application took to start up was not critical.

Now, let’s consider container-based applications. New containers may be started automatically by orchestration software. The application in the container should then be ready for immediate use. In addition, several redundant containers are often launched for one service. The reduction in resource consumption achieved with Quarkus is multiplied accordingly.

RedHat manager Alex Handy sums it up like this:

Quote

“When you think of serverless computing, microservices and the [...] cloud, there’s one language you’re probably not [thinking of]: Java. And that’s a real shame. [...] Java was and is the workhorse language of business. It remains the third most popular language in the world […] It’s been the language of choice for corporations that need to keep a single application up and running for years at a time.”

- Alex Handy, Source: thenewstack.io/quarkus-gives-spring-boot-users-a-path-to-serverless-and-live-coding/

The advantages of Quarkus are obvious. However, the framework also has some limitations. As such, Quarkus is not primarily intended to migrate existing Java applications. Instead, it is worth using Quarkus as a starting point for new development. We will look at a few specific areas of application below. In all of the examples mentioned, Maven or Gradle is used as the build tool. The area of application is determined by configuring the ‘mvn’ or ‘gradle’ command. The build tool then automatically generates the required configurations and artifacts.

Executing microservice applications in Kubernetes with Java and Quarkus

Kubernetes is an orchestration software for container applications. Using Kubernetes with Docker containers is quite common. Individual services of an application are saved as Docker images and managed by Kubernetes. The orchestrator takes over the management of the containers generated from the images: Kubernetes starts, controls, and monitors the services. Often, multiple copies of a service are launched for load-sharing and increased fault tolerance. If one of the services crashes, the container is destroyed and a new container is created from the same image. Java Quarkus includes the configurations that are necessary for use in Kubernetes.

Implementing REST APIs and serverless applications with Java and Quarkus

REST is the long-established architecture style for web applications. APIs in particular are mostly implemented following this approach. A REST-API is based on client-server architecture. Communication takes place via the HTTP protocol using the “verbs” GET, POST, PUT, DELETE. These correspond to the well-known CRUD “verbs” (“create, read, update, delete”) from the database environment. Data exchange between an API and a user usually takes place via JSON.

Serverless computing is an alternative architecture for cloud-based applications. In this model, also known as “Function as a Service” (FaaS), a single function runs briefly in a container. The function is called up, performs a calculation, and is then switched off again. Despite the name, the serverless functions continue to run on servers. Programmers no longer need to worry about them. With AWS Lambda, Google Cloud Functions, and Microsoft Azure Functions, serverless environments are available on all major cloud platforms. Java code can be used on these platforms with Quarkus.

Tip

Create your own REST-API on a dedicated server from IONOS.

Building reactive web apps with Java and Quarkus

In contrast to imperative programming, reactive programming represents a modern programming paradigm. The actions that should take place when certain events occur are described by a programmer. The best-known representatives of this programming style are the frameworks “React” and “Vue”, written in JavaScript. Both of them focus on the creation of web-based user interfaces. With Quarkus, applications can be implemented in an imperative and reactive style. It is even possible to combine both paradigms.

Where is Quarkus used?

Quarkus was designed with the aim of optimizing Java applications for use in containers and cloud environments. The possibility of compiling a Java program directly into machine code, however, opens up even more exciting application possibilities. Let’s have a look at the most interesting current areas of application for Quarkus.

First, let’s remember how a Java program developed with Quarkus runs. During the build process, the Java source code is compiled into bytecode, which is then translated into machine code when it is executed. Bytecode can be generated with Quarkus, which is then executed in a Java runtime environment, such as the HotSpot VM, via interpretation or just-in-time (JIT) compilation. Depending on the configuration, various performance-relevant optimizations come into play.

On the other hand, GraalVM, based on HotSpot, can be used to generate a native image using ahead-of-time (AOT) compilation. The native image is a binary file that contains all of the libraries and dependencies necessary to run the application. Since no JVM is required for execution, the greatest performance gains are won from AOT compilation.

Java applications in container environments

Kubernetes is usually involved when using a Java app in containers. A Java app packaged as a Docker image can also be used on an OpenShift cluster. You can test the use of Quarkus with Kubernetes, for example, by using a minikube installation on your local system.

Java functions in serverless environments

Use Quarkus to easily implement a function written in Java in serverless environments from Amazon, Google, and Microsoft.

Java programs in embedded systems

With the ability to create a native image from a Java application, Java code can also be used in embedded systems. AOT compilation is used here, which ensures low memory consumption and fast start-up times for a specific application.

Tip

Use Managed Kubernetes from IONOS for your container apps.

Quarkus compared to other frameworks

Quarkus is suitable for a wide range of different application scenarios. Other frameworks are more specific to some extent. Let’s look at a couple of similar alternatives:

  • React: This JavaScript framework has established itself as the standard for reactive programming.
  • Open Liberty: This IBM framework allows for the development of microservice applications with Java. Like Quarkus, Open Liberty comes with a live reload functionality.
  • Micronaut: With the Micronaut framework, microservices and serverless applications can be programmed in Java. As with Quarkus, GraalVM is used here.
  • Spring/Spring Boot: Spring is probably the most popular Java framework for web applications. Spring is based on GraalVM and, in addition to the creation of microservices, it supports reactive programming and live reload. In a performance comparison, Quarkus beat Spring. An existing Spring project can be migrated to Quarkus relatively easily.

What are the pros and cons of Quarkus?

The main advantage of developing Java applications with Quarkus is a gain in performance. This is particularly important when using Java applications in container environments. Performance benefits include:

  • Fast application start-up time
  • Low memory consumption
  • Almost immediate scaling of services
  • Lower space requirements for native images

In addition to the performance advantages, Quarkus shines because of its user-friendliness. Experienced Java EE and Spring developers can easily learn to use the framework. They also benefit from the fact that Quarkus is based on a solid framework. The following standard technologies are used, in addition to others:

  • Eclipse MicroProfile
  • Spring Dependency Injection
  • Hibernate ORM

Quarkus also offers a live coding environment, in which developers can quickly prototype. The live reload feature contributes to smooth development. After activating the dev mode, changes to the source code and configuration are compiled in the background. The developer only has to reload the browser window to understand the changes.

Finally, we conclude with the disadvantages of using Quarkus. These mostly result from the optimizations that come into play during compilation.

  • In particular, reducing the dynamic information generated during runtime can lead to problems in some scenarios.
  • The severely limited possibilities for introspection may make it difficult to debug an application.
  • The highly-optimized build process for native images takes a long time.

Quarkus is not intended for just any Java project. To some extent, using this framework requires processes to be converted.