Stable microservices are not always literally essential for survival, but imagine you live in an area where there are poisonous snakes, and you have many many detectors in your backyard warning you about them. The detectors are communicating via a Wifi connection and REST interface with your server - so this communication should be really robust.
How would you write the code for the server-side microservice?
Which technologies and languages would you use?
In this series I'll show how to implement resilient microservices using Kotlin and Quarkus. This is the first part of the series giving a short introduction to the technologies we are going to use and why they are a good choice implementing the stated scenario.
Why Kotlin?
First of all, let's see why we can benefit from using Kotlin as a primary language. Kotlin had its first official 1.0 release in 2016. Already then it had many improvements over the current state of Java at that time like:
- Build-in null safety
- Data classes
- Native support for functional programming
- Extension functions
- Smart casts
just to name a few. Here is a small example of Kotlin in use:
data class User(
// non null property
val name: String,
// Int instead of int; no usage of primitives.
// But compiled to Javas int
val age: Int,
// nullable property
val description: String?
)
val user = User(
// named parameters
name = "John Smith",
age = 30,
description = null
)
// destructuring of data classes
val (name, age, description) = user
val descriptionToPrint = description
// null safe access to description with ?.
?.let { "the following description: $it" }
// fallback value for a null description
?: "no description"
// String builder
println("$name is $age old and $descriptionToPrint")
// prints: John Smith is 30 old and no description
On top of that there are many more features available to play around with. Since its early releases the language kept on evolving and new features were added. Most importantly Coroutines and Flow as well as more recent entries into the language like
- Value classes
- Contracts
or one of the newest additions Context Receivers, still in an experimental development phase. On top of that it fits quite nicely into my personal ecosystem, which is driven by the facts that:
- I'm mainly a mobile developer
- I have a strong interest on cross-platform solutions
- I like the Android platform the most
- Most apps work better with a dedicated backend for frontend, so I write those too.
Kotlin provides everything needed for this.
In the native world, Kotlin gained a lot of tracking with the announcement of Google to make it a first class citizen for Android development and later on the leading language, mostly replacing Java in that area.
More recently Kotlin also introduced a solution for cross-platform development in Kotlin Multiplatform, which enabled Kotlin to run the the same code across Android, iOS, Web and Desktop.
Since Java is still strong in the area of backend development many of the frameworks used there are either able to be used with Kotlin or have additional support for Kotlin's unique features. Such frameworks for example are Quarkus, Spring Boot or Micronaut. Also already matured frameworks that are native to Kotlin, like Ktor, are available for developers to choose from.
Why Quarkus?
Let's have a closer look on Quarkus and why it can help us to fulfil our requirements. What is Quarkus? It provides an easy approach to create a scalable backend on an enterprise level.
Generally speaking it's a Cloud Native Java stack, which is built with many mature and well known (open source) Java libraries, frameworks and standards. So instead of learning everything new, we can use for example Vert.x, Camel, Hibernate and RESTEasy. In case of standards we can find MicroProfile and selected specifications from Jakarta EE. Dependency injection uses the well known CDI. Looking at annotations, we can use JAX-RS annotations when defining REST endpoints. On one hand JPA annotations can be used to map persistent entities. On the other hand JTA annotations are there to declare transaction boundaries with Hibernate. Last but not least there is SmallRye to configure, secure and monitor our application.
They say it's
supersonic subatomic Java
Puh Java what?!
Instead of Java only, it also works well with GraalVM and Kotlin YAY!
We will have a closer look on how to do this, later.
What's the idea behind Quarkus?
The idea behind Quarkus is to optimize Java for cloud native environments.
This is done by several optimizations during build time, a failed startup intention and the fact that it has Vert.x underneath.
Sounds quite good when running apps in containers!
What does Quarkus to optimize the build time? During build time Quarkus removes everything which is not needed. Additionally dependency injections are analysed and already injected at compile time - so we could say it is hard wired.
With their fast startup intention, Quarkus realizes very fast start up times, which is ideal for cloud native environments: when scaling up for launching more containers or in a Recovery scenario for restarting containers.
Additionally because Quarkus has Vert.x underneath, it relies on the reactor pattern, so it can handle a lot of requests with ease. Good for us when a lot of requests are coming in.
From my perspective
Quarkus offers hot reload, which is not self evident in the Java world, but feels like state of the art nowadays. I usually use it on a daily bases, so it is pretty important to me.
Quarkus has an active and supportive community, where we can contribute and exchange information rapidly. Having a feature request yourself to e.g. enhance Kotlin support? Just talk to them and in the best case offer a PR yourself. You will surely be heard.
With a long history of writing Java backends, Quarkus makes me also feel at home with their alignment to the JEE specs (Annotations, Extensions, Frameworks, Libraries ...) and especially with RESTeasy. This even does not cause any performance disadvantages, as everything is mapped to Quarkus cloud native at build time. But in contrast to JEE approach to handle annotation processing during runtime, Quarkus is performing these operations during build time which drastically improves the performance of the quarkus app.
If that is not enough, you are still able to compile Quarkus to a native binary which improves the performance even more.
Imperative and reactive
Quarkus gives us the freedom to implement our application in an imperative style, which is well known from JEE, but it also allows to use a more reactive approach - leveraging the power of Kotlin Flows.
Imperative (REST endpoint)
@Path("greeting")
class GreetingEndpoint(
private val greetingService: GreetingService
) {
@GET
@Produces(MediaType.TEXT_PLAIN)
fun greeting(@QueryParam("username") username: String?) =
greetingService.greet(username)
}
Reactive (Server sent events)
@GET
@Produces(MediaType.SERVER_SENT_EVENTS)
@RestSseElementType(MediaType.TEXT_PLAIN)
fun stream(): Flow<String> = flow {
emitAll(channel.asFlow())
}
Easy project setup through code generation
Getting started is quite easy by visiting https://code.quarkus.io/. As a first step start searching for Kotlin and select the related checkbox.
After that go searching for RESTeasy-reactive and make sure that the related checkbox is also selected.
Nice! Our app is ready for download.
Now we can start writing our first endpoint.
@Path("greeting")
class GreetingEndpoint(
private val greetingService: GreetingService
) {
@GET
@Produces(MediaType.TEXT_PLAIN)
fun greeting(@QueryParam("username") username: String?) =
greetingService.greet(username)
}
The code sample above gives us a GET endpoint with a query parameter for the username and produces a greeting response message with media type TEXT_PLAIN.
To make it run just execute
quarkus dev
in your command line. Use a rest client like postman to test the response.
But how to make this resilient?
We'll see that in Part 2 by highlighting the resilience design patterns that do the most to reduce recovery times and down times - stay tuned.
In the meantime, if you want to try my welcome example from above, you can find it on Github.
Top comments (0)