|
From Quarkus in Action by Martin Štefanko and Jan Martiška This book teaches you to build resilient and scalable cloud applications with Quarkus. It’s written by Martin Štefanko and Jan Martiška, Red Hat engineers who are both active contributors to the Quarkus project. Learn how to implement your applications with cutting-edge technologies in Quarkus including gRPC and GraphQL, and reveal how Quarkus eases deployment with Kubernetes and OpenShift. |
Java is one of the most popular programming languages utilized for developing enterprise systems. With its vast ecosystem of libraries, frameworks, standards, runtimes, and most importantly us, the developers, Java represents a genuine choice for building modern, robust, and scalable software.
However, many of these systems often solve similar challenges. This is why they often rely on some underlying technology that provides solutions to these problems. Whether the composition of the system consists of a set of JARs, WARs, or EARs (Java, Web, or Enterprise Archives) deployed on an application server, or whether it follows the more recent microservices architecture, there are multiple choices of Java frameworks and libraries that the system can utilize.
Quarkus is a Java framework that targets microservices and serverless system development. Quarkus emerged as an alternative to the existing Java microservices stacks to provide an application framework that delivers an unmatched performance benefits while still providing a development model utilizing the APIs (Application-Programming interfaces) of popular libraries and Java standards that the Java ecosystem has been practicing for years. Quarkus also puts a strong focus on developer productivity because, as developers know a technology’s productivity and usability are what is appreciated the most.
In this book, we focus on delivering a concise learning experience of the Quarkus framework assuming no prior Quarkus experience and gradually building towards a completely developed enterprise system consisting of multiple Quarkus microservices. Our priority is to explain the main concepts, tools, and features of Quarkus, so by the end of the book you understand the values that Quarkus provides and can also assess the Quarkus applicability for your projects.
Who is this book for?
This book is for all software developers with basic Java enterprise application development knowledge. Other JVM (Java Virtual Machine) languages are also a good starting point since Quarkus also allows you to develop with, for instance, Kotlin or Scala (however, we focus on Java in this book). The individual chapters of this book dive into different technologies, some of which are quite new. You will find that Quarkus is a great learning tool for such purposes. So if we encounter a more recent technology that you might not know yet, we also explain the basic concepts in addition to its utilization in Quarkus to provide the complete picture.
Introducing Quarkus
The name Quarkus consists of two parts — quark and us. A quark is an elementary particle that constitutes matter aligning with the very small resource footprint that Quarkus aims to provide. The second part, “us”, comprises us developers, engineers, and operations who utilize various software development processes where Quarkus creates as useful environments as possible.
Quarkus.io website describes Quarkus as a “Kubernetes Native Java stack tailored for OpenJDK HotSpot and GraalVM, crafted from the best of breed Java libraries and standards” (https://quarkus.io). But what does this really mean? Let’s break down the definition and describe in depth what Quarkus is, what problems it tries to solve, and why you should care about learning it.
A Kubernetes native stack
Let’s start with Kubernetes native. This warrants a quick Kubernetes (https://kubernetes.io) introduction. As enterprise applications and services started to move from physical servers to the cloud, the need to encapsulate the application with its execution environment (i.e., the operating system and dependencies) became prominent. This led to the evolution of container technologies made mainstream by Docker (https://docker.io). With containers becoming increasingly popular, the need for container management appeared, and that’s precisely what Kubernetes is: the de-facto standard container management platform.
Figure 1 visualizes the relationships between applications, containers, and the management platform. The user-provided containers lifecycle and resourcing are delegated to the Kubernetes platform. The technologies that run inside the containers might differ. Kubernetes treats the container as a unit without knowing the container’s internals.
In addition to any application exposed entry point (e.g. an HTTP API), the containers can also define some optional hooks that help Kubernetes with their management. For instance, the application can expose a health check endpoints that Kubernetes uses to decide whether the container should be restarted or if it can consume requests (Quarkus provides this functionality for you if you need it). Figure 1 depicts a standard JVM container which, except for the user application, also packages the actual JVM on which the application runs. In turn, the JVM requires some other dependencies, for instance, glibc
, which the container also needs to include.
Delegating management of the application lifecycle to Kubernetes also requires a way to make dynamic decisions in terms of configuring our application. The configuration of the individual containers might be different even for the same application replicated in multiple containers (e.g. a unique identifier). For this reason, Docker containers and consecutively, the Kubernetes platform allows us to override the configuration values inside the respective containers with environment variables. These can be implicitly deducted from the container specification as, for instance, the JAVA_HOME shown in Figure 1 or we can manually pass them to the container when it’s starting. Changing the configuration manually passed in the environment variables thus also means that the Kubernetes restarts the container with new values.
Figure 1. Visualizing the relationships between the application, container, and Kubernetes.
We use the term Kubernetes native
to describe tools and frameworks specifically targeting Kubernetes as the target deployment platform. Does this label imply that the stack targets are exclusively related to Kubernetes? No, it’s mostly a way to communicate that the development team has taken the extra effort to ensure a positive user experience on Kubernetes (or cloud in general).
So, in what ways is Quarkus Kubernetes native? By being designed for containers as the main packaging format utilized in cloud and by providing a tool-set that allows you to build and deploy containers in a single step. Additionally, Quarkus also supports a series of features that promote integration with the cloud platforms (e.g. exposing health-related information, externalizing configuration directly in the platform, etc.), which are integral for a positive user experience in the cloud environment.
Designed for containers
The software industry has been skeptical about the use of Java inside containers. Containers need to start quickly, consume few resources, be small, and Java frameworks typically don’t live up to the task. Quarkus, on the other hand, has been designed to create applications that start quickly and have excellent efficiency and performance. It uses a concept of build time processing to move as much processing as possible from runtime to compile time. This is an approach popularized by Google Dagger, a dependency injection framework for Java and Android. For mobile applications, runtime performance and resource utilization are really important as they directly impact the user experience. So, Dagger had to push as much processing possible from runtime to compile time, to reduce response times and battery consumption. In enterprise applications, battery consumption may be irrelevant. Still, the memory footprint is not as it can directly impact the production costs since it is one of the critical factors affecting cloud computing pricing.
Single step deployments
Many developers despise writing configuration files, deployment manifests, or anything else expressed in a markup language. However, Kubernetes deployments do require writing such manifests. Some feel it’s not part of their role, and others find it boring. Still, all of these developers might be relieved to learn that Quarkus comes with its own tools that allow your application to be packaged into a container which can then be shipped to Kubernetes as part of your application build without requiring developers to compose the manifests themselves. This feature is unique to Quarkus, which saves time and guarantees that the experience is optimized for each application. In other words, the framework understands the needs of your application that it requires from its platform and expresses them by tuning the deployment process accordingly.
OpenJDK HotSpot and GraalVM
OpenJDK HotSpot refers to the Java Virtual Machine (JVM), the most common runtime for Java applications. GraalVM is a JVM and development kit distribution that brings improved performance, support for multiple languages, and native image compilation to the table. GraalVM is composed of a set of different layers which compared to the traditional JVM, provides a way of also supporting non-JVM languages (e.g. JavaScript or Python). We focus on the GraalVM compiler and the native image compilation which are the main parts of GraalVM that Quarkus utilizes.
Native compilation allows users to build a standalone binary executable that runs without requiring a JVM. It represents simplified application distribution and provides an execution environment where the application no longer has to wait for the JVM to start, which decreases the application startup time. Such native executables start in tens of milliseconds which is incredibly fast for any Java application. So why don’t we compile all our Java code into native binaries? The main problem is that the process of creating a native image is often tedious as it requires a lot of configuration and tuning. Quarkus helps significantly on this front, as we demonstrate throughout this book.
The important decision applications need to make is whether they should stick to JVM (OpenJDK) or make a move to the GraalVM native compilation. The correct answer is that there is no “one size fits all” solution. Services that need to optimize on startup time (e.g. serverless) prefer native compilation, while the ones that need to optimize the throughput (microservices) might perform better on JVM.
For Quarkus, it is essential to provide a framework that can work equally well in both scenarios. It provides almost all required configurations for the successful GraalVM compilation of your application which makes it very easy to switch between the JAR and native. The build of native executable is then trivial as it only requires using a flag at build time without needing additional code changes or configuration files (as is the case for most alternative frameworks).
Libraries and standards
The last needed part of the definition is the best of breed java libraries and standards, which refers to the wide range of popular libraries and enterprise Java standards supported by Quarkus both in JVM and native mode. As we mentioned before, compiling applications into native binaries is a tedious process. Adding third-party libraries to your application makes it even more complex. Quarkus provides native compilation support for the most popular libraries and standards implementations available in the Java ecosystem. Furthermore, this support is not limited just to the native mode. Even in the JVM mode, Quarkus defines sensible configuration defaults (convention over configuration) and provides innovative features improving the manipulation and ease of use of the library in your code that you’ll discover in the chapters to come.
Quarkus uses standards like MicroProfile (https://microprofile.io/) or Jakarta EE (Enterprise Edition, https://jakarta.ee/) and popular open-source frameworks such as Hibernate, Vertx, Apache Camel, or RESTEasy (https://hibernate.org/, https://vertx.io/, https://camel.apache.org, https://resteasy.dev/). This allows developers to reuse their expertise and years of practice with these libraries when they start working with Quarkus.
Open-source standards alleviate the need for applications to be tightly coupled to a single vendor since multiple vendors implement the standard. Practically, this means that teams can move from other frameworks supporting MicroProfile or Jakarta EE standards to Quarkus without the need to re-implement everything from scratch. This is not just because the users utilize the same APIs they already know, but also because it presents easier migration paths of existing modules. For instance, if the application already uses standards like MicroProfile, the migration might not even require code changes. What if we told you that you could even port chunks of code written using Spring Boot APIs? Quarkus also provides limited support for a few Spring APIs to ease the migration paths for developers that need to adjust to the MicroProfile and Jakarta EE APIs, which might be new to them.
Principles of Quarkus
Now that we’ve broken down the definition of Quarkus, let’s discuss a few more important principles that Quarkus builds upon:
- Imperative and reactive programming seamlessly connected together
- Making developers’ lives easier
Imperative and reactive programming seamlessly connected together
Most developers are used to writing code in an imperative model, which involves writing a sequence of statements that describe how the program operates. Lately, an alternative paradigm called reactive programming has been gaining popularity. Reactive programming is a paradigm that utilizes asynchronous data streams that allow your software to be performant, scale dynamically, sustain heavy load, and react to events declaratively.
Quarkus has excellent support for both imperative and reactive programming. Under the hood, Quarkus has a reactive engine. However, it doesn’t force you to use the reactive programming model. Users can still program imperatively or even combine the two paradigms (even in the same application). Using “just enough reactive” is helpful for developers and teams that are new to the reactive programming model and need to ease into it.
Reactive programming has become an important alternative to the imperative paradigm that needs to be considered when the developed application expects to be responsive and scalable also under heavy user traffic. This is why we demonstrate the use of reactive programming in the example application developed throughout the book.
Making developers’ lives easier
We can devote whole chapters to talking about performance characteristics, programming paradigms, and standards, but what good are they for if you, the developer, can’t enjoy what you are doing? People may argue about the importance of developer experience. Still, if you take a moment to think about it, you’ll realize that many software stacks and even programming languages have been created with the sole purpose of the developer experience in mind.
Quarkus brings to the table a pleasant development model which boosts developers’ productivity with features that make tedious repeating software development tasks obsolete. Quarkus provides a feedback loop that allows developers to test their code as they develop it without having to restart the actual application or perform any other kind of ceremonial tasks. Throughout this book, we refer to this loop as the “Development mode” (aka Dev mode). This feedback loop not only makes the development processes faster but it’s also an essential learning tool. Developers get to see what works and what doesn’t very fast, free of repetitiveness, which helps to learn things quickly.
This kind of feedback loop is pretty common among interpreted languages (e.g. JavaScript). It allows developers to write code and see their changes take immediate effect without rebuilding or reloading. However, it is not common in compiled languages like Java. With all Quarkus features encompassed in this development loop, Quarkus provides a unique development experience similar to scripting languages. But Quarkus does this with compiled JVM languages like Java or Kotlin.
What will readers learn in this book?
- How to use Quarkus’ Dev mode to speed up the development of Java applications and how to make it much more enjoyable.
- How to use the Dev UI to gain valuable insights into a running application for troubleshooting purposes.
- The utilization of the Continuous testing feature, where tests run automatically in the background as the developer updates the code, without interrupting the development workflow.
- 4. Several new frameworks and libraries they might not be familiar with yet, like Reactive Messaging, gRPC, or GraphQL.
- Simplified deployment of applications into Kubernetes/OpenShift, supplied by tools provided by Quarkus.
- Further simplification of development provided by Quarkus which automatically provides and manages temporary instances of remote services like databases and message brokers.
- Compiling JVM applications into native binaries using GraalVM, made much easier by making use of Quarkus’ tooling.
Summary
- Quarkus is a full-stack Java framework for developing modern enterprise applications (monolithic, microservices, or serverless).
- Quarkus has been built from the ground up with containers and Kubernetes in mind.
- Since Quarkus supports many well-known standards and popular libraries, the learning curves are often short.
- Quarkus provides excellent support for building native applications.
- The new development workflow called Dev mode introduced in Quarkus adds to productivity and can be a great learning tool.
- This book will teach you how to leverage the power of Quarkus for your applications!
If you want to learn more about the book, check it out here.