- 8 minutes read

Such a bold mission statement. Supersonic subatomic Java. And I'm tempted to fall for the marketing. Everything I've seen and heard about Quarkus sounds just great. So it's time to question the marketing and to examine Quarkus thoroughly.

Official Quarkus logoThat's what I'm going to do during the next couple of weeks. This article is an early assessment, so maybe it's sort of a living document. I'm going to edit it every once in a while when I learn more about Quarkus.

What is Quarkus, and what makes it stand out among other frameworks?

Why do we need Quarkus at all?

Recently, Java Software architecture has become sort of boring. The kitchen sink architecture consists of JavaScript in the frontend and stateless Spring Boot REST services in the backend.

Standard microservice architecture. (C) 2020 by Stephan RauhThe advent of powerful JavaScript UI frameworks and cloud computing encouraged this standard architecture. Funny thing is they've become the key drivers to overcome this standard, too. Spring Boot only gets you to a certain point. But there's more. Node.js shows what's possible. That's why node.js has become the de-facto standard for Amazon Lambda services.

Using Java for a Lambda service is a no-go. Granted, it's possible, and I know people doing just that. But the key idea of Lambda services is to shut them down when they are no longer necessary. There's no point in wasting resources nobody needs. So the next unhappy user of your service is going to endure a cold-start of your service. Using a framework like Spring Boot, this takes a couple of seconds. That doesn't sound much, but it's ages if your Lambda service is simply part of a large mesh of services. A request that's answered in a few milliseconds during business hours may take a minute in the evening.

Developers usually start pinging the service regularly just to keep it alive. Of course, that's the wrong solution. Remember, the idea of the Lambda service is to free resources as often as possible, allowing other users to benefit from them.

What we need is an architecture enabling us to write Lambda services in our pet language. Well, it's already there. Node.js does an excellent job. But many companies have invested heavily in Java (or Kotlin, or Scala), so they aren't ready to switch to another programming language.

GraalVM to the rescue

Enter GraalVM. If you've been following our series about GraalVM, you know that GraalVM is a remarkable alternative to the standard OpenJDK. It's an impressive piece of tech. But from a Java programmers point of view, it's not a big deal. It's a plug-in replacement without much impact on the performance of your application.

Unless you're using the AOT compiler (aka "native compilation"). Granted, it won't make traditional programs run faster. If you're into game development, the native compilation is going to disappoint you. You'll end up with fewer features and mediocre performance. Nothing beats the JIT compiler when it comes to optimizing long-running applications.

But that's missing the point. Native compilation cuts down start-up times and memory consumption. That's what we need for our Lambda service. The Achilles heel of Java has always been the cold-start time. The AOT compiler of GraalVM makes this a thing of the past. For small programs - such as Lambda services - is eliminates cold start almost entirely. A simple REST service is available after roughly 0.01 seconds. How cool is that?

Bye, bye, reflection!

Why don't we just compile our Spring Boot application with GraalVM?

Well, Spring is older than GraalVM. So it has optimized for other goals. As far as I know, the Spring Boot team is diligently working on GraalVM support, but there's one big problem: native compilation doesn't play well with reflection. And reflection is one of the pillars Spring Boot relies on.

Luckily, GraalVM has an ace up its sleeve. The AOT compiler supports reflection by adding an additional compilation phase. It analyzes your application in advance, trying to predict where reflection is needed and where it's not. That, in turn, makes compilation painfully slow.

I'm sure the Spring Boot team is going to solve this challenge one day or another, but Quarkus has been developed with this restriction in mind. It gives you all the reflection-based tools we've come to love, such as Hibernate, dependency injection, and much more, but at a lower price.

Containers, Docker, and Kubernetes

Cloud computing changes the way we think about programming in other ways, too. Nowadays, our applications usually run in light-weight Docker containers. The idea is to start new containers when necessary quickly and to kill them off immediately when our users stop using our application. So the design goal is to keep the container size small and cute.

When Spring and JavaEE were conceived, the world looked entirely different. At the time, we wrote big, monolithic applications running in an application server. That was a big machine able to run dozens of applications. At least, that was the theory. It never worked out well. So Spring Boot was the first approach to change that. One application server per application. I've written an article about the topic that became pretty popular at the time.

So the old working theory was that we have to use every option our CPU gives us to improve performance. Among other things, that means using every core of the CPU, which lead to multithreading.

Multithreading is a flawed concept. It's great, but few programmers can juggle with multiple threads properly. Plus, multithreading means switching contexts frequently, and every context switch takes some time. It costs performance.

Non-blocking IO

The answer is to use a single thread. That's a programming model every programmer can wrap their head around. Node.js made this idea popular.

At first glance, using a single thread sounds like wasting the other cores of your CPU. However, in a container architecture, that's not the case. Just start a container for every core of your CPU. That's achieving the same goal with a much simpler programming model.

Non-blocking IO takes this idea to another level. If you're waiting for the response of a database server, you don't have to block the CPU. In the meantime, it can serve other users' requests. Basically, that's the old idea of cooperative multitasking.

The price to pay is reactive programming. That's a considerable price. It's counter-intuitive, so to many programmers, that's like learning a new programming paradigm. Well, it is. It may feel natural after practicing a lot, but don't underestimate the transition phase.

That's one of the things Quarkus excels at. It supports and embraces reactive programming, and it's built upon a reactive foundation. But it doesn't force you to use reactive programming. If you insist, if you're willing to sacrifice performance, you can stick to the traditional blocking programming model.

The idea is to allow for a best-of-breed approach: use the simple blocking IO for the bulk of your application, and use reactive programming for the performance-critical parts of your program.

Sometimes the difference between the two worlds even blurs. For example, when reading the tutorial about reactive routes, you'll see little difference to traditional JAX-RS programming. But it's based on the non-blocking Vert.x framework.

Wrapping it up

After all this talk about containers and reactive programming - how difficult is it to learn Quarkus?

To my surprise, it's easy to adopt Quarkus. The majority of Quarkus consists of popular frameworks. There's Hibernate; there's CDI; there's JAX-B. And so on. There's also Vert.x for reactive programming. That's old tech, too, but chances are you're not familiar with it. That's why it's optional. Using it is highly recommended, but not required.

There's also a lot a well-written, helpful documentation and code.quarkus.io, a service resembling Spring Initializr. Putting it all together, my first impression is that using Quarkus is easy.

The only (slightly) disturbing thing I've noticed about Quarkus is that it doesn't support UI. There's support for servlets, but little beyond that. There's no Spring MVC, no JSF, no Ozark (aka JavaEE MVC). UI programming doesn't feel like a first-class citizen in Quarkus nation.

The age of microservices has arrived. Java has moved entirely to the backend. UI programming has become the domain of the younger generation preferring JavaScript.


Dig deeper

Quick overview over the key features of Quarkus

code.quarkus.io the application skeleton generator of Quarkus

Imperative and reactive programming in Quarkus

Introduction to reactive routes (the counterpart of JAX-RS)

Using Vert.x

Reactive API in Quarkus, including non-blocking database queries


Comments