How Quarkus combines MicroProfile and Spring

Hello everyone, and here is the third post from the series about Quarkus!

How Quarkus combines MicroProfile and Spring

When developing Java microservices, it is often assumed that Eclipse MicroProfile ΠΈ Spring boot are separate and independent APIs. By default, programmers tend to use the APIs they are already used to, as learning new frameworks and runtime components is time consuming. Today we will try to simplify the development of some popular MicroProfile API for Spring Developers and show how to use the Spring API and new useful features at the same time quarkus.

In a little more detail, we will first look at the scope and details of how Quarkus supports the Spring APIs to show Spring developers how to use the MicroProfile API in their daily work. Then we'll talk about the MicroProfile API, which will be useful for Spring developers when creating microservices.

Why Quarkus? Firstly, this is live coding, that is, automatic reloading of any changes in the MicroProfile API, Spring API and other Java APIs, which is performed with just one command: mvn quarkus:dev. Second, considered in our example The Person service (it compiles from the Spring, MicroProfile and JPA APIs to binary using the native GraalVM image) starts in just 0.055 seconds and consumes about 90MB of RAM (RSS) on the RESTful application endpoint. Moreover, its compilation itself is performed with just one command: mvn package -Pnative.

We will not delve into the details of MicroProfile, but will only try to help Spring developers understand how Quarkus can use the Spring APIs along with the MicroProfile APIs.

Containers and Kubernetes

In order not to overload this article, we will only cover the high-level aspects of support here. Kubernetesbecause it is important to understand. Quarkus is positioned as a Java stack for Kubernetes, it is designed to minimize the memory consumption and startup time of Java applications and services, and as a result, increase their density on the host and reduce overall costs.

Quarkus also supports auto generation Kubernetes resources and offers leadership for deployment on Kubernetes and Red Hat OpenShift platforms. In addition, Quarkus automatically generates the Dockerfile.jvm (JVM packaging) and Dockerfile.native (native binary packaging) files needed to create containers.

And finally, focusing on Kubernetes as a target deployment environment, Quarkus does not use Java frameworks in cases where similar functionality is implemented at the level of the Kubernetes platform itself. Table 1 provides a functional mapping between Kubernetes and typical Java frameworks used by Spring developers.

Table 1. Functional map of Java frameworks and Kubernetes.

Functional
Traditional Spring Boot
Kubernetes

Service discovery
Eureka
DNS

Configuration
Spring Cloud Configuration
Config Maps/Secrets

load balancing
Ribbon (client side)
Service, Replication Controller(server side)

Compiling and running the code from the example

In this article we refer to sample project, where the Spring and MicroProfile APIs are shared, and even the same Java class. The code in this example can be compiled and run from the command line, see the README.md file for details.

Spring Framework APIs

Dependency Injection

Quarkus supports a range of Contexts and Dependency Injection (CDI) APIs and the Spring Dependency Injection (Spring DI) APIs. If you are working with MicroProfile, Java EE and Jakarta EE, you are already familiar with CDI. On the other hand, Spring developers can use the Quarkus Extension for Spring DI API to be compatible with Spring DI. Examples of using the supported Spring DI APIs are shown in Table 2.

Π’ project from our example used by both CDI and Spring Dependency Injection. For more information and examples on this topic, see the Quarkus guide called Spring DI Guide.

Table 2. Examples of using the supported Spring DI APIs.

Supported features of Spring DI
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 users will love that Quarkus supports JAX-RS, MicroProfile Rest Client, JSON-P, and JSON-B as the core web programming model. Spring developers will be pleased with the recent support for the Spring Web API in Quarkus, in particular the interfaces responsible for REST. Similar to Spring DI, the main goal of supporting the Spring Web API is to allow Spring developers to use the Spring Web APIs along with the MicroProfile APIs. Examples of using the supported Spring Web APIs are shown in Table 3, and more information and examples on this topic can be found in the Quarkus guide called Spring Web Guide.

Table 3. Examples of using the supported Spring Web APIs.

Supported features of Spring Web
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 DataJPA

MicroProfile users will also appreciate that Quarkus supports JPA using the Hibernate ORM. There is good news for Spring developers too: Quarkus supports common Spring Data JPA annotations and types. Examples of using the supported Spring Data JPA APIs are shown in Table 4.
Π’ project from our example uses the Spring Data JPA APIs, and more information is available in the Quarkus guide titled Spring Data JPA Guide.

Table 4. Examples of using the supported Spring Data JPA APIs.

Supported features of Spring Data JPA
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 constructs are very important for preventing cascading failures and building robust microservice architectures. Spring developers have been using circuit-breakers for fault tolerance for many years. hystrix. However, Hystrix has not been updated for a long time, but MicroProfile's Fault Tolerance is now actively developing and has several years of production use behind it. Therefore, to improve the reliability of services in Quarkus, it is recommended to use the MicroProfile Fault Tolerance APIs, examples of which are given in Table 5. For more information on this, see the Quarkus guide. Fault Tolerance Guide.

Table 5. MicroProfile Fault Tolerance supported API usage examples.

MicroProfile Fault Tolerance Features
Description
Examples

@Asynchronous

Running logic on a separate thread

@Asynchronous
@Retry
public Future<String> getSalutation() {
   ...
   return future;
}

@Bulkhead

Limiting the number of concurrent requests

@Bulkhead(5)
public void fiveConcurrent() {
   makeRemoteCall(); //...
}

@CircuitBreaker

Smart failover and failover

@CircuitBreaker(delay=500   // milliseconds
   failureRatio = .75,
   requestVolumeThreshold = 20,
   successThreshold = 5)
@Fallback(fallbackMethod = "fallback")
public String getSalutation() {
   makeRemoteCall(); //...
}

@fallback

Calling alternative logic in case of failure

@Timeout(500) // milliseconds
@Fallback(fallbackMethod = "fallback")
public String getSalutation() {
   makeRemoteCall(); //...
}

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

Retry

Retry on request failure

@Retry(maxRetries=3)
public String getSalutation() {
   makeRemoteCall(); //...
}

timeout

Control timeout on failure

@Timeout(value = 500 )   // milliseconds
@Fallback(fallbackMethod = "fallback")
public String getSalutation() {
   makeRemoteCall(); //...
}

Checking services (Service Health)

Kubernetes platforms monitor the health of containers using special services. In order for the underlying platform to monitor services, Spring developers typically use a custom HealthIndicator and Spring Boot Actuator. In Quarkus, this can be done using MicroProfile Health, which by default perform a liveness check, but can also be configured to simultaneously check liveness and readiness (readiness). Examples of how to use the supported MicroProfile Health APIs are shown in Table 6, and more information is provided in the Quarkus manual. Health Guide.

Table 6. Usage examples for supported MicroProfile Health APIs.

MicroProfile Health Features
Description
Examples

@liveness

The platform reloads failed containerized applications
end point:
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

The platform will not send traffic to containerized applications if it is not ready
end point:
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 provide metrics for either operational purposes (to monitor performance SLAs) or non-operational purposes (business SLAs). Spring developers provide metrics using the Spring Boot Actuator and Micrometer. In turn, Quarkus uses MicroProfile Metrics to provide base metrics (JVM and operating system), vendor metrics (Quarkus), and application metrics. MicroProfile Metrics requires the implementation to support JSON and OpenMetrics (Prometheus) output formats. Examples of using the MicroProfile Metrics API are shown in Table 7.

Π’ project from our example MicroProfile Metrics are used to provide application metrics. More information can be found in the Quarkus guide Metric Guide.

Table 7. Examples of using the MicroProfile Metrics APIs.

Functions of MicroProfile Metrics
Description
Examples

@Counted

Indicates a counter that counts the number of calls to the annotated object

@Counted(name = "fallbackCounter", 
  displayName = "Fallback Counter", 
  description = "Fallback Counter")
public String salutationFallback() {
   return fallbackSalutation;
}

@ConcurrentGauge

Indicates a gauge that counts the number of concurrent calls to the annotated object

@ConcurrentGuage(
  name = "fallbackConcurrentGauge", 
  displayName="Fallback Concurrent", 
  description="Fallback Concurrent")
public String salutationFallback() {
   return fallbackSalutation;
}

@Gauge

Indicates a gauge that measures the value of an annotated object.

@Metered(name = "FallbackGauge",
   displayName="Fallback Gauge",
   description="Fallback frequency")
public String salutationFallback() {
   return fallbackSalutation;
}

@metered

Indicates a meter that monitors the call frequency of the annotated object

@Metered(name = "MeteredFallback",
   displayName="Metered Fallback",
   description="Fallback frequency")
public String salutationFallback() {
   return fallbackSalutation;
}

Metric

An annotation containing information about metadata when a request is received to contribute or produce a metric

@Metric
@Metered(name = "MeteredFallback",
   displayName="Metered Fallback",
   description="Fallback frequency")
public String salutationFallback() {
   return fallbackSalutation;
}

Timed

Denotes a timer that keeps track of the duration of the annotated object

@Timed(name = "TimedFallback",
   displayName="Timed Fallback",
   description="Fallback delay")
public String salutationFallback() {
   return fallbackSalutation;
}

Metrics Endpoints

Application Metrics localhost:8080/metrics/application
Basic Metrics localhost:8080/metrics/base
Vendor metrics localhost:8080/metrics/vendor
All metrics localhost:8080/metrics

MicroProfile Rest Client

Microservices often provide RESTful endpoints that require appropriate client APIs to work with. To use RESTful endpoints, Spring developers typically use RestTemplate. Quarkus offers MicroProfile Rest Client APIs for this task, examples of which are given in Table 8.

Π’ project from our example using RESTful endpoints is done with the MicroProfile Rest Client. For more information and examples on this topic, see the Quarkus guide Rest Client Guide.

Table 8. MicroProfile Rest Client API usage examples.

MicroProfile Rest Client Features
Description
Examples

@RegisterRestClient

Registers a typed Java interface as a REST client

@RegisterRestClient
@Path("/")
public interface MyRestClient {
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String getSalutation();
}

@RestClient

Marks the injection of an instance of a typed REST client interface

@Autowired // or @Inject
@RestClient
MyRestClient restClient;

Summoner

Calls a REST endpoint

System.out.println(
   restClient.getSalutation());

mp-rest/url

Specifies a REST endpoint

application.properties:
org.example.MyRestClient/mp-rest/url=
   http://localhost:8081/myendpoint

Results

In this blog, primarily for Spring developers, we briefly looked at how to use the Spring APIs in Quarkus along with the MicroProfile APIs to develop Java microservices and then compile them into native binary that saves hundreds of megabytes of RAM. and starts in milliseconds.

As you already understood, additional information about support for the Spring and MicroProfile APIs, as well as a lot of other useful information, can be found in Quarkus manuals.

Source: habr.com

Add a comment