Quarkus

Eclipse MicroProfile and Spring Boot are often thought of as separate and distinct APIs when developing Java microservices. Developers default to their mental muscle memory by leveraging the APIs that they use on a daily basis. Learning new frameworks and runtimes can be a significant time investment. This article aims to ease the introduction to some popular MicroProfile APIs for Spring developers by enabling them to utilize the Spring APIs they already know while benefiting from significant new capabilities offered by Quarkus.

More specifically, this article covers the scope and details of the Spring APIs supported by Quarkus so Spring developers have a grasp of the foundation they can build on with MicroProfile APIs. The article then covers MicroProfile APIs that Spring developers will find helpful in the development of microservices. Only a subset of MicroProfile is covered.

Why Quarkus? Live coding is one reason, where any change is automatically reloaded whether MicroProfile, Spring, or any other Java API. Just run mvn quarkus:dev. That's it. A second compelling reason is that the example project's Person service, which compiles Spring, MicroProfile, and JPA APIs to a native binary using GraalVM's native-image, starts in 0.055 seconds and uses ~90MB of RAM (RSS) after hitting the application RESTful endpoints. Run mvn package -Pnative to compile to a native binary. That's it.

This article will not go into detailed comparisons, but should give the Spring developers an understanding of how the Spring and MicroProfile APIs can be used together with Quarkus.

Containers and Kubernetes

This article will only cover Kubernetes support at a high level to keep this article short(er), but it is important to briefly discuss. One of the Quarkus key value propositions is "Kubernetes-native Java," where the goal is to minimize the memory footprint and reduce the startup time. The reduced memory footprint helps drive up the density of applications on the same hardware, reducing overall costs.

Quarkus also supports auto-generation of a Kubernetes resources, and guides are available on deploying to Kubernetes and Red Hat OpenShift as well. Additionally, a Dockerfile.jvm (JVM packaging) and Dockerfile.native (native binary packaging) are generated automatically for container creation.

Last, given that Quarkus considers Kubernetes a target deployment environment, it forgoes using Java frameworks when inherent Kubernetes capabilities are available. Table 1 briefly maps Java frameworks typically used by Spring developers with Kubernetes built-in capabilities.

Table 1: Java framework to Kubernetes mapping

Service, Replication Controller("server side")

Capability Traditional Spring Boot Kubernetes
Service discovery Eureka DNS
Configuration Spring Cloud Config Config Maps / Secrets
Load balancing Ribbon ("client side") Service, Replication Controller("server side")

Compiling and running the example code

This article is accompanied by an example project that utilizes Spring and MicroProfile APIs together in the same project, and even the same Java class. The code can be compiled and run from the command line. Be sure to read the README.md for instructions.

Spring Framework APIs

Dependency Injection

Quarkus supports many Contexts and Dependency Injection (CDI) APIs and Spring Dependency Injection (Spring DI) APIs. MicroProfile, Java EE, and Jakarta EE developers will be very familiar with CDI. Spring developers, on the other hand, can use the Quarkus Extension for Spring DI API for Spring DI compatibility. Table 2 covers a sample of the supported Spring DI APIs.

The example project utilizes both CDI and Spring Dependency Injection, and the Quarkus Spring DI Guide goes into greater detail along with additional examples.

Table 2: Sample of supported Spring DI APIs

Spring DI
Supported Feature

Examples
Constructor Injection
public PersonSpringController(
   PersonSpringRepository personRepository,  // injected      
   PersonSpringMPService personService) {    // injected
      this.personRepository = personRepository;
      this.personService = personService;
}
Field Injection
@Autowired
@Value
@Autowired
@RestClient
SalutationRestClient salutationRestClient;

@Value("${fallbackSalutation}")
String fallbackSalutation;
@Bean
@Configuration
@Configuration
public class AppConfiguration {
   @Bean(name = "capitalizeFunction")
   public StringFunction capitalizer() {
      return String::toUpperCase;
   }
}
@Component
@Component("noopFunction")
public class NoOpSingleStringFunction implements StringFunction {
   @Override
   public String apply(String s) {
      return s;
   }
}
@Service
@Service
public class MessageProducer {
   @Value("${greeting.message}")
   String message;

   public String getPrefix() {
      return message;
   }
}

Web framework

MicroProfile developers will be comfortable with the Quarkus support for JAX-RS, MicroProfile Rest Client, JSON-P and JSON-B as the core web programming model. Spring developers may be surprised to know that Quarkus has recently added Spring Web API support, specifically around Spring REST-related APIs. As with Spring DI, the goal of Spring Web API support is to make Spring developers feel at home using Spring Web API and MicroProfile APIs together. Table 3 covers a sample of supported Spring Web APIs.

The example project utilizes Spring Web and MicroProfile Rest Client APIs, and the Quarkus Spring Web Guide goes into greater detail along with additional examples.

Table 3: Sample of supported Spring Web APIs

Spring Web
Supported Feature

Examples
@RestController
@RequestMapping
@RestController
@RequestMapping("/person")
public class PersonSpringController {
   ...
   ...
   ...
}
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
@RequestParam
@RequestHeader
@MatrixVariable
@PathVariable
@CookieValue
@RequestBody
@ResponseStatus
@ExceptionHandler
@RestControllerAdvice (partial)
@GetMapping(path = "/greet/{id}",
   produces = "text/plain")
   public String greetPerson(
   @PathVariable(name = "id") long id) {
   ...
   ...
   ...
}

Spring Data JPA

MicroProfile developers will be comfortable with the Quarkus JPA support using Hibernate ORM. Spring developers, have no fear! Quarkus has support for commonly used Spring Data JPA annotations and types. Table 4 covers a sample of supported Spring Data JPA APIs.

The example project utilizes the Spring Data JPA repository APIs, and the Quarkus Spring Data JPA Guide goes into greater detail along with additional examples.

Table 4: Sample of supported Spring Data JPA APIs

Spring Data JPA
Supported Feature

Examples
CrudRepository
public interface PersonRepository
         extends JpaRepository,
                 PersonFragment {
   ...
}
Repository

JpaRepository

PagingAndSortingRepository

public class PersonRepository extends 

    Repository {

    Person save(Person entity);

    Optional findById(Person entity);
}
Repository Fragments
public interface PersonRepository
         extends JpaRepository,
                 PersonFragment {
   ...
}
Derived query methods

 

public interface PersonRepository extends CrudRepository {

    List findByName(String name);
    
    Person findByNameBySsn(String ssn);
    
    Optional 
       findByNameBySsnIgnoreCase(String ssn);

    Boolean existsBookByYearOfBirthBetween(
            Integer start, Integer end);
}
User-defined queries
public interface MovieRepository
         extends CrudRepository {

    Movie findFirstByOrderByDurationDesc();

    @Query("select m from Movie m where m.rating = ?1")
    Iterator findByRating(String rating);

    @Query("from Movie where title = ?1")
    Movie findByTitle(String title);
}

MicroProfile APIs

Fault tolerance

Fault tolerance patterns are critical to prevent cascading failures and to create a reliable microservice architecture. Hystrix circuit-breaking has been a "go-to" fault tolerance pattern for Spring developers for years. However, Hystrix is in maintenance mode. MicroProfile Fault Tolerance is in active development and developers have been using it in production for years now. Quarkus recommends using MicroProfile Fault Tolerance APIs to improve service reliability. Table 5 covers a sample of the MicroProfile Fault Tolerance APIs.

The example project uses the MicroProfile Fault Tolerance API, @Timeout and @Fallback in particular. The Quarkus Fault Tolerance Guide goes into greater detail along with additional examples.

Table 5: Sample of MicroProfile Fault Tolerance APIs

MicroProfile Fault Tolerance
Feature

Description Examples
@Asynchronous Execute logic on a separate thread
@Asynchronous
@Retry
public Future<String> getSalutation() {
   ...
   return future;
}
@Bulkhead Limits number of concurrent requests
@Bulkhead(5)
public void fiveConcurrent() {
   makeRemoteCall(); //...
}
@CircuitBreaker Gracefully handle faults and fault recovery
@CircuitBreaker(delay=500   // milliseconds
   failureRatio = .75,
   requestVolumeThreshold = 20,
   successThreshold = 5)
@Fallback(fallbackMethod = "fallback")
public String getSalutation() {
   makeRemoteCall(); //...
}
@Fallback Alternative logic called upon failure
@Timeout(500) // milliseconds
@Fallback(fallbackMethod = "fallback")
public String getSalutation() {
   makeRemoteCall(); //...
}

public String fallback() {
   return "hello";
}

@Retry Retry a request
@Retry(maxRetries=3)
public String getSalutation() {
   makeRemoteCall(); //...
}
@Timeout Wait period before assuming failure
@Timeout(value = 500 )   // milliseconds
@Fallback(fallbackMethod = "fallback")
public String getSalutation() {
   makeRemoteCall(); //...
}

Service Health

Platforms like Kubernetes utilize probes to check the health of a container. Spring developers utilize a custom HealthIndicator and Spring Boot Actuator to expose the health of a service to the underlying platform. With Quarkus, Spring developers can utilize MicroProfile Health to expose the health of a service. A default liveness check is provided, and developers can provide custom liveness and readiness checks as well. Table 6 covers a sample of the MicroProfile Health APIs.

The example project uses MicroProfile Health to expose the readiness of an application. The Quarkus Health Guide goes into greater detail along with additional examples.

Table 6: Sample of MicroProfile Health APIs

MicroProfile Health
Feature

Description Examples
@Liveness Platform will reboot unhealthy containerized applications.

Endpoint:
host:8080/health/live

@Liveness
public class MyHC implements HealthCheck {
  public HealthCheckResponse call() {

   ...
   return HealthCheckResponse
     .named("myHCProbe")
     .status(ready ? true:false)
     .withData("mydata", data)
     .build();  
}
@Readiness Platform will not direct traffic to containerized applications that are not ready.

Endpoint:
host:8080/health/ready

@Readiness
public class MyHC implements HealthCheck {
  public HealthCheckResponse call() {

   ...
   return HealthCheckResponse
     .named("myHCProbe")
     .status(live ? true:false)
     .withData("mydata", data)
     .build();  
}

Metrics

Applications expose metrics for operational reasons (like performance SLAs) and non-operational reasons (like business SLAs). Spring developers typically utilize Spring Boot Actuator and Micrometer to expose metrics. Quarkus utilizes MicroProfile Metrics to expose base (JVM & Operating System), Vendor (Quarkus), and application metrics. MicroProfile Metrics requires JSON and OpenMetrics (Prometheus) output formats be supported by implementations. Table 7 covers a sample of the MicroProfile Metrics APIs.

The example project uses MicroProfile Metrics to expose the application metrics. The Quarkus Metrics Guide goes into greater detail along with additional examples.

Table 7: Sample of MicroProfile Metrics APIs

MicroProfile Metrics
Feature

Description Examples
@Counted Denotes a counter which counts the invocations of the annotated object.
@Counted(name = "fallbackCounter", 
  displayName = "Fallback Counter", 
  description = "Fallback Counter")
public String salutationFallback() {
   return fallbackSalutation;
}
@ConcurrentGauge Denotes a gauge which counts the parallel invocations of the annotated object.
@ConcurrentGuage(
  name = "fallbackConcurrentGauge", 
  displayName="Fallback Concurrent", 
  description="Fallback Concurrent")
public String salutationFallback() {
   return fallbackSalutation;
}
@Gauge Denotes a gauge, which samples the
value of the annotated object.
@Metered(name = "FallbackGauge",
   displayName="Fallback Gauge",
   description="Fallback frequency")
public String salutationFallback() {
   return fallbackSalutation;
}
@Metered Denotes a meter which tracks the frequency of invocations of the annotated object.
@Metered(name = "MeteredFallback",
   displayName="Metered Fallback",
   description="Fallback frequency")
public String salutationFallback() {
   return fallbackSalutation;
}
@Metric An annotation that contains the metadata
information when requesting a metric to
be injected or produced.
@Metric
@Metered(name = "MeteredFallback",
   displayName="Metered Fallback",
   description="Fallback frequency")
public String salutationFallback() {
   return fallbackSalutation;
}
@Timed Denotes a timer which tracks duration of
the annotated object.
@Timed(name = "TimedFallback",
   displayName="Timed Fallback",
   description="Fallback delay")
public String salutationFallback() {
   return fallbackSalutation;
}
Metrics Endpoints
Application metrics http://localhost:8080/metrics/application
Base metrics http://localhost:8080/metrics/base
Vendor metrics http://localhost:8080/metrics/vendor
All metrics http://localhost:8080/metrics

MicroProfile Rest Client

Microservices often expose RESTful endpoints, requiring a client API to consume a RESTful endpoint. Spring developers typically use a RestTemplate to consume RESTful endpoints. Quarkus supports the MicroProfile Rest Client API to do the same. Table 8 covers a sample of MicroProfile Rest Client APIs.

The example project uses MicroProfile Rest Client to consume RESTful endpoints. The Quarkus Rest Client Guide goes into greater detail along with additional examples.

Table 8: Sample of MicroProfile Rest Client APIs

MicroProfile
Rest Client
Feature
Description Examples
@RegisterRestClient Register a typed Java interface as a REST client
@RegisterRestClient
@Path("/")
public interface MyRestClient {
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String getSalutation();
}
@RestClient Decorate  instance injection of a typed REST client interface
@Autowired // or @Inject
@RestClient
MyRestClient restClient;
Invocation Invoke REST endpoint
System.out.println(
   restClient.getSalutation());
mp-rest/url Specify rest endpoint
application.properties:
org.example.MyRestClient/mp-rest/url=
   http://localhost:8081/myendpoint

Summary

This article provided an overview, primarily for Spring developers, on using Spring APIs together and MicroProfile APIs with Quarkus. Spring developers can now use some of the APIs they know and love, combined with MicroProfile APIs, to live code Java microservices and then compile them to a native binary savings 100's of MB of RAM while starting in milliseconds.

Note: The Quarkus guides provide more details around Spring and MicroProfile API support, and much more.

Last updated: July 1, 2020