10 Aug 2022

Lightweight and blazing-fast Java app using Quarkus and Redis®*

Traditional Java apps are notorious for being large and slow to start. Read on to see how to use Quarkus and Redis®* to make a Kubernetes-friendly Java app.

dewan-ahmed

Dewan Ahmed

|

RSS Feed

Senior Developer Advocate

Lightweight and blazing-fast Java app using Quarkus and Redis®* illustration

When building applications that run on Kubernetes, you want the start-up time and footprint to be small. Traditional Java applications like to consume more than their constrained memory and CPU and you might have seen OOMKilled errors in your production applications. Brace yourself as this blog takes you on a "supersonic" journey with Quarkus (a fast and lightweight Java framework) and Redis®* (fast in-memory data structure store).

Imagine that you're building a one-time-password feature as part of a larger project. Your team is using Java, which they're familiar with, and Quarkus as the framework because they want the application to be Kubernetes-compatible (in terms of size and startup time).

Besides a relational database, the team is also using Redis for fast querying of the generated one-time-passwords. This example uses Aiven for Redis and you can sign up for a free trial if you want to try it out.

An important consideration for your team is that both Quarkus and Redis are open-source projects.

To follow the hands-on portion of this blog, here are the software requirements:

The following command creates a new Maven project and adds the required extensions to your project. The Quarkus redis-client extension allows the user to connect to a Redis server and run Redis commands. The resteasy-jackson extension allows to build RESTful web services and to process JSON data format. The resteasy-mutiny extension helps to build reactive APIs for asynchronous systems.

mvn io.quarkus.platform:quarkus-maven-plugin:2.7.5.Final:create \ -DprojectGroupId=org.acme \ -DprojectArtifactId=one-time-password \ -Dextensions="redis-client,resteasy-jackson,resteasy-mutiny" \ -DnoCode cd one-time-password

Use the following command to add the redis-client dependency to your pom.xml file:

./mvnw quarkus:add-extension -Dextensions="redis-client"

Create an Aiven for Redis instance. For this exercise, any service plan will do. Once the Redis service is running, copy the Service URI from the Overview > Connection information tab.

Go to your favorite code editor and open the one-time-password project. Paste the Service URI to the src > main > resources > application.properties file:

quarkus.redis.hosts=[YOUR REDIS CONNECTION INFORMATION GOES HERE]

If you're running Redis on your local machine, use the following settings:

quarkus.redis.hosts=redis://localhost:6379

The Otp class creates a POJO (Plain Old Java Object) to hold the session_key and otp_value. The generateRandomOtp method generates a random value within a given range.

Create the src/main/java/org/acme/redis/Otp.java file and add the following:

package org.acme.redis; import java.util.Random; public class Otp { final int lowRange = 100000; final int highRange = 999999; public String session_key; public int otp_value; public Otp(String session_key) { this.session_key = session_key; this.otp_value = generateRandomOtp(lowRange, highRange); } public Otp() {} private static int generateRandomOtp(int low, int high) { // Generate random int value from $low to ($high - 1) return low + new Random().nextInt(high - low); } }

You are going to create the OtpService which will play the role of a Redis client. This class will help you perform the GET, EXISTS, SETEX, SETNX, and TTL Redis commands. For documentation on these see the official Redis commands page.

Create the src/main/java/org/acme/redis/OtpService.java file and add the following:

package org.acme.redis; import io.quarkus.redis.client.RedisClient; import java.util.Arrays; import javax.inject.Inject; import javax.inject.Singleton; @Singleton class OtpService { final int timeInSeconds = 20; @Inject RedisClient redisClient; public String getOtp(String session_key) { return redisClient.get(session_key).toString(); } public void newOtp(String session_key) { Otp otp = new Otp(session_key); // SETNX will only create a key if it doesn't already exist // - so we won't overwrite an existing OTP value // Unfortunately SETNX can't set the TTL/expiration time if (redisClient.setnx(otp.session_key.toString(), String.valueOf(otp.otp_value)).toBoolean()) { // Only update TTL/expiration if the OTP value was set redisClient.setex(otp.session_key.toString(), String.valueOf(timeInSeconds), String.valueOf(otp.otp_value)); } } public String getOtpTTL(String session_key) { return redisClient.ttl(session_key).toString(); } public boolean keyExists(String session_key) { return redisClient.exists(Arrays.asList(session_key)).toBoolean(); } }

Create the src/main/java/org/acme/redis/OtpResource.java file where you define the HTTP endpoints for your one-time-password service.

Observe the @Inject annotation to easily create the service instance and @Path("/otp") to indicate the creation of the HTTP endpoints. The same endpoint is used for both GET and POST calls. The great thing of using a framework like Quarkus is that without the annotations, you would have to write multiple lines of code to achieve the same outcome.

package org.acme.redis; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import org.jboss.resteasy.annotations.jaxrs.QueryParam; import io.vertx.core.json.JsonObject; @Path("/otp") public class OtpResource { @Inject OtpService service; @GET public JsonObject getOtp(@QueryParam String session_key) { // If the key doesn't exist, return an error response rather than the usual // object response if (!service.keyExists(session_key)) { return errorResponse(); } JsonObject result = new JsonObject(); result.put("OTP: ", service.getOtp(session_key)); result.put("TTL: ", service.getOtpTTL(session_key)); return result; } JsonObject errorResponse() { JsonObject result = new JsonObject(); result.put("Message: ", "The OTP key doesn't exist."); return result; } @POST public void newOtp(@QueryParam String session_key) { service.newOtp(session_key); } }

From the root of your one-time-password project, execute the following command:

./mvnw quarkus:dev

Navigate to http://localhost:8080/otp?session_key=[SOME_KEY], replacing [SOME_KEY] with any text. For your initial run, you'll receive an error response like this:

"Message: ": "The OTP key doesn't exist."

This is because this key doesn't exist in Redis yet. From your terminal, make the following POST request to create your key:

curl --location --request POST 'http://localhost:8080/otp?session_key=[SOME_KEY]'

Now reload your browser and you should receive a random 6-digit number as a one-time-password that expires in 20 seconds. If you wait more than 20 seconds, it will expire and you'll receive the error response again.

If you keep refreshing the page, you'll be getting the same OTP until the (time-to-live) TTL runs out.

You might also realize that you didn't have to do any server setup/handling for this application as Quarkus took care of that for you. Pretty cool, eh?

In this blog, I provided you with a getting started experience with Redis and demoed a use case by building a simple one-time-password application using the Quarkus framework. The design and implementation of actual one-time-password software is far more complex than the simple application mentioned here; for instance, it would probably not want to give the same OTP to different users.

To learn more about Quarkus:

To learn more about Redis:

Related blogs

All things open source, plus our product updates and news in a monthly newsletter.

Subscribe to the Aiven newsletter

Loading...