25 April 2021

Serverless with Quarkus

A game changer in the java space?

Header image

Amazon began pioneering the concept of serverless architecture back in 2014 with the introduction of AWS Lambda. It may not have taken the world by storm, but interest is growing in the pay-as-you-go model that enables the execution of small units of work without the need to provision, scale, and maintain servers.

In the serverless world - where code is loaded on demand and charges are in gigabyte-seconds - a premium is placed on lighting-quick startup times, low-memory utilization and functions that execute quickly. Scripting languages like Python and Node.js, perceived as most suitable under these constraints, have dominated the space thus far. It’s fair to say that Java has been slow to adapt itself. Red Hat engineer Jason Greene sums it up nicely:

...we now live in a world dominated by the cloud, mobile, IoT, and open source, where containers, Kubernetes, microservices, reactive, Function-as-a-Service (FaaS), 12-factor, and cloud-native application development can deliver higher levels of productivity and efficiency. As an industry, we need to rethink how Java can be best utilized to address these new deployment environments and application architectures. Around this time last year, the release of Micronaut saw a serious attempt to push Java into the serverless space. Boasting not only fast startup times and low memory consumption, but also a programming model familiar to Spring enthusiasts. Earlier this month, Red Hat announced their own solution: Quarkus.

Quarkus is a Kubernetes Native Java framework tailored for GraalVM and HotSpot, crafted from best-of-breed Java libraries and standards. The goal of Quarkus is to make Java a leading platform in Kubernetes and serverless environments while offering developers a unified reactive and imperative programming model to optimally address a wider range of distributed application architectures. This caused quite a stir on Twitter; “Game changer” and “The future of Java applications” were some of the statements being made. No doubt we can expect to see more of Quarkus at the major Java conferences in 2019.

Until then, we thought we may as well ride the wave of hype and take a look at what Quarkus is all about. We have a short guide where we'll run a basic application and highlight some appealing developer features, like live-reloading (code → refresh browser → repeat), dependency injection and support for both imperative and reactive programming styles. And finally, to make it a little more interesting - and because it's one of our favorite tool kits - we’ll attempt to integrate Vert.x as well.

Quick guide: Quarkus and Vert.x

In this short guide we'll create the standard "greeting" application but make use of a Vert.x service to handle the greeting functionality itself.

All the code can be found in GitHub.

Step

A basic project can be created with Maven (version 3.5.3+ ) like so:

mvn io.quarkus:quarkus-maven-plugin:0.11.0:create \
 -DprojectGroupId=team.quad \
 -DprojectArtifactId=quarkus-vertx

Quarkus comes with two installed features by default: RESTEasy and ArC, a CDI-based dependency injection solution tailored for the Quarkus architecture.

We want to be able to inject a Vert.x instance to a REST endpoint. Adding the Vert.x extension is easily done with Maven too.

mvn quarkus:add-extension -Dextensions=vertx

Now we're ready to code something up.

Code

Creating a REST endpoint in Vert.x is a bit more clunky than in other frameworks. With Quarkus we get back the familiar way of annotating our classes and methods to create endpoints. To make use of Vert.x, we can simply inject an instance, but since we only need to forward on a message, we can just inject the Vert.x. event bus itself. The intention here is to make use of the event bus to offload the business logic (such that it is) to another service.

@Path("/hello")
public class GreetingResource {

@Inject
EventBus eventBus;

@GET
@Produces(MediaType.TEXT_PLAIN)
public CompletionStage<String> sayHello(@QueryParam("name") String name) {
return eventBus.<String>send("say-hello", name)
.thenApply(Message::body);
}

}

Here we're sending the name we get from our incoming request as a message to whatever is listening on the "say-hello" address. The response is handled asynchronously.

public class GreetingService {

@ConsumeEvent("say-hello")
public CompletionStage<String> sayHello(String name) {
return CompletableFuture.supplyAsync(() -> "Hello " + name);
}

}

Consuming from the Vert.x event bus is made even simpler in Quarkus than Vert.x itself. Annotate a method to indicate which event bus address to listen to and that's more or less it!

Run

Compile and start the application:

mvn compile quarkus:dev

Starting the application in this way allows us to make changes to the code without restarting the application. A feature that comes right out of the box!

Once running, it's ready to receive a request:

curl localhost:8080/hello?name=Quarkus


Hello Quarkus!

Testing

When it comes to testing, rapid startup time means we can afford to start the entire application before each unit test, eliminating the artificial separation between unit and functional tests that we're used to with other frameworks. Just annotate with @QuarkusTest.

Let’s create a unit test that will check our greeting endpoint:

@QuarkusTest
public class GreetingResourceTest {

@Test
public void test_greeting_gives_200_OK_and_correct_response_body() {
given()
.when().get("/hello?name=Quarkus")
.then()
.statusCode(200)
.body(is("Hello Quarkus"));
}
}

Quarkus supports JUnit 4 and JUnit 5.

Container deployment

One feature that Quarkus provides is the ability to build a native executable for our application out of the box by using GraalVM.

Let’s build it using Docker:

mvn package -Pnative -Dnative-image.docker-build=true

This produces a native 64 bit Linux executable. When building without Docker, it’s mandatory to have GRAALVM_HOME set for the build machine.

Build the docker image:

docker build -f src/main/docker/Dockerfile -t quarkus-vertx/guide .

And now we can run it:

docker run -i --rm -p 8080:8080 quarkus-vertx/guide

Push it to a Docker registry of your preference and get it deployed into the cloud. No hassle.

Conclusion

Quarkus feels both familiar and new at the same time. It's easy to get started and has a lightweight feel about it. There's a lot more to explore in the coming months and we're interested to see how it develops.

See you soon!

Join a team

Heb je een teamlid, collega of vriend met wie je het liefst blijft ontwikkelen, meld je dan samen aan!

orange-arrow-leftorange-arrow-centerorange-arrow-rightorange-arrow-leftorange-arrow-right

Contact Mark

Geen recruiter maar een developer, net zoals jij.