Introduction to Quarkus Reactive Architecture with HTTP Example

Knoldus Inc.
4 min readJul 18, 2022

--

Quarkus is reactive. It’s even more than this: Quarkus unifies reactive and imperative programming. You don’t even have to choose: you can implement reactive components and imperative components then combine them inside the very same application. No need to use different stacks, tooling, or APIs; Quarkus bridges both worlds.

What is Imperative programming?

Imperative programming is a software development paradigm where functions are implicitly coded in every step required to solve a problem. In imperative programming, every operation is coded and the code itself specifies how the problem is to be solved, which means that pre-coded models are not called on.

Imperative programming requires an understanding of the functions necessary to solve a problem, rather than a reliance on models that are able to solve it. The focus of imperative programming is how the problem should be solved, which requires a detailed step-by-step guide. Because the written code performs the functions instead of models, the programmer must code each step. Procedural and object-oriented programming (OOP) languages fall under imperative programmings, such as C, C++, C#, and Java.

What is Reactive Programming?

Reactive programming uses declarative code to create asynchronous processing pipelines. In other words, it’s programming with asynchronous data streams that transmits data to a consumer as it becomes available, allowing developers to design code that responds quickly and asynchronously.

Reactive Systems as distributed systems having four characteristics:

  • Responsive — they must respond in a timely fashion
  • Elastic — they adapt themselves to the fluctuating load
  • Resilient — they handle failures gracefully
  • Asynchronous message passing — the component of a reactive system interacts using messages

Reactive Systems and Quarkus

Reactive System is an architectural style that can be summarized by: distributed systems done right. Relying on asynchronous message passing helps enforce the loose coupling between the different components. You send messages to virtual destinations. The receiver can be located anywhere, or even not yet exist at the time of the message dispatch. The elasticity pillar allows scaling up and down individual components according to the load. Elasticity also provides redundancy, which helps with the resilience pillar. Failures are inevitable. Components forming a reactive system must handle them gracefully, avoid cascading failures, and self-adapt themselves.

A responsive system can continue to handle the request while facing failures and under fluctuating load. Quarkus have been tailored for that. It provides features that will help you design, implement and operate reactive systems.

HTTP APIs in Quarkus

Quarkus has, since its genesis, been able to serve HTTP API.

package org.knoldus;

import javax.ws.rs.GET;
import javax.ws.rs.Path;

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

@GET
public String hello() {
return "Hello";
}
}

RESTEasy classic invokes the HTTP endpoint on a worker thread associated with the HTTP request. It is a well-understood model, simple to understand. However, relying on worker threads introduces a concurrency limit: the number of threads.

The main difference between RESTEasy classic and reactive is how they call the HTTP endpoint methods:

classic — always on a worker thread

reactive — on the I/O thread or on a worker thread

Threads are expensive, especially in containers or on the cloud where the resources are limited. Using the I/O threads avoids creating additional threads (improving memory consumption) and avoids context switches (improving response time)

When to use blocking annotation and when not to use it?

When we introduced RESTEasy reactive, we decided to use a non-blocking approach by default: if not stated otherwise, it calls the HTTP endpoint method on the I/O thread. This model resulted in outstanding performance and was simple enough, thanks to the usage of the @Blocking annotation. As Hibernate ORM classics blocking, you can’t use it with RESTEasy reactive without using the @Blocking annotation. This annotation changes the dispatching strategy to use a worker thread (instead of the I/O thread)

Basically: synchronous methods default to worker threads, and asynchronous methods default to I/O threads, except if explicitly stated otherwise. Of course, you can override the behavior using the @Blocking and @NonBlocking annotations

Hello RESTEasy Reactive Example

package org.knoldus;

import io.smallrye.common.annotation.NonBlocking;

import javax.ws.rs.GET;
import javax.ws.rs.Path;

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

@GET
@NonBlocking
public String hello() {
return "Hello";
}
}
With the RESTEasy reactive, it would have been called on the I/O thread.

Conclusion

With Quarkus 2.2, the dispatching strategy of RESTEasy reactive becomes smarter thus improving the developer experience.

  • You don’t need to learn the reactive way; you can keep using imperative code.
  • You don’t need to think about your threads; Quarkus does that for you.

References:

--

--

Knoldus Inc.

Group of smart Engineers with a Product mindset who partner with your business to drive competitive advantage | www.knoldus.com