Microservices Framework Battle: Is Quarkus a SpringBoot Replacement?

1Overview

The SpringBoot framework needs no introduction, Java programmers must know it. Relatively speaking, there may be fewer people familiar with Quarkus. The slogan posted on the Quarkus homepage: Supersonic Subatomic Java.

It is a Kubernetes Native Java framework tailor-made for OpenJDK HotSpot and GraalVM, built on best-of-breed Java libraries and standards. The arrival of Quarkus brings an innovative platform for developing Linux containers and Kubernetes native Java microservices.

In this article, we will make a simple comparison of these two Java frameworks, Spring Boot and Quarkus. We can better understand their similarities and differences, as well as some of their peculiarities. We also perform some tests to measure their performance. Finally, we’ll cover how a developer can transition from Spring to Quarkus.

2SpringBoot

Spring Boot is a Java-based framework focused on enterprise applications. It makes it easy to use all Spring projects and integrates many out-of-the-box features to help developers become more productive.

Spring Boot reduces the amount of configuration and boilerplate code. In addition, due to its convention over configuration approach, it automatically registers default configurations based on dependencies, greatly shortening the development cycle of Java applications.

3Quarkus

Quarkus is another framework that takes a similar approach to Spring Boot above, but with the added advantage of delivering smaller artifacts with faster startup times, better resource utilization and efficiency (Supersonic, Subatomic).

It is optimized for cloud, serverless and containerized environments. Although with a slightly different focus, Quarkus also integrates well with the most popular Java frameworks.

4Comparison

As mentioned above, both frameworks have great integration with other projects and frameworks. However, their internal implementation and architecture are different. For example, Spring Boot provides two types of web functionality: Blocking (Servlets) and non-blocking (WebFlux).

Quarkus, on the other hand, also provides both methods, but unlike Spring Boot, it allows us to use both blocking and non-blocking methods. Additionally, Quarkus embeds areactiveprogramming approach into its architecture.

To get more accurate data in our comparison, we will use two fully reactive applications implemented using Spring WebFlux and Quarkus reactive features.

In addition, one of the most important features of the Quarkus project is the ability to create Native Images (executable binaries based on a specific platform). Therefore, we will also include two native images in the comparison, but Spring’s native image support is still experimental. In addition we need to use GraalVM.

Test application

Our application will implement three APIs: one that allows users to create zip codes, another that looks up information for a specific zip code, and finally that queries zip codes by city. These APIs are implemented using the reactive approach of Spring Boot and Quarkus mentioned earlier, and the database uses PostgreSQL.

Our goal is to create a sample program that is slightly more complex than the HelloWorld program. Of course, the implementation of things like database drivers and serialization frameworks will affect our comparison results. However, most applications will probably need to handle these things.

Therefore, the purpose of the comparison is not to prove which framework is better or more efficient, but to analyze a case study of these specific implementations.

Test plan

To test both implementations, we will use JMeter to execute the tests and analyze their test reports. Additionally, we will use VisualVM to monitor the application’s resource utilization during test execution.

The test will run for 5 minutes, calling all APIs, starting with a warm-up period and then increasing the number of concurrent users until it reaches 1,500. We will start populating the database in the first few seconds and then start querying like this:

e7e4793e011e8f54b4c088836715ab89.jpeg
1cc9a408751701378f8e1251b7253cea.jpeg

All tests were performed on machines with the following specifications:

1bb4abe3f25060445cbb9f4defd156a3.jpeg

The end result may be less than ideal due to the lack of isolation from other background processes, but as mentioned earlier, we do not intend to conduct an extensive and detailed analysis of the performance of these two frameworks.

5 Survey results

Both projects are a great experience for developers, but it’s worth mentioning that Spring Boot has better documentation and more can be found online. Quarkus is improving in this area, but is still a bit behind.

In terms of indicators, we have the following results:

919f0ebf21525ebeda796325b28ce8a0.jpeg

Through this experiment, we can observe that Quarkus is almost twice as fast as Spring Boot in terms of startup time for both JVM and native versions. Build times are also much faster. In the case of the native image, the build takes: 9 minutes (Quarkus) vs. 13 minutes (Spring Boot), and the JVM build takes: 20 seconds (Quarkus) vs. 39 seconds (Spring Boot).

The same story happens with artifact size, with Quarkus again leading the way by generating smaller artifacts. Native image: 75MB (Quarkus) vs. 109MB (Spring Boot), and JVM version: 4KB (Quarkus) vs. 26MB (Spring Boot).

Regarding other indicators, the conclusion is not so obvious. So we need to look a little deeper.

CPU

We see that the JVM version consumes more CPU at the beginning of the warm-up phase. After that, the CPU usage stabilized, with all versions consuming relatively equally.

The following is the CPU consumption of Quarkus in JVM and Native versions:

e09438bc384b29e053efbf1788bc92d4.jpeg

JVM version of Quarkus ↑↑↑
303c9b8f9ef851c8b89a15d21a62643d.jpeg
Native version of Quarkus ↑↑↑

Memory

Memory is even more complicated. First of all, it is obvious that the JVM versions of both frameworks reserve more memory for the Heap. Nonetheless, Quarkus reserves less memory from the start, as does memory utilization during startup.

Then, looking at the utilization during testing, we can observe that the Native version does not seem to reclaim memory as efficiently or as frequently as the JVM version. This can be improved by adjusting some parameters, in this comparison we used the default parameters and made no changes to GC, JVM options or any other parameters.

Let’s take a look at the memory usage graph:

79efb9c2b4183c57c8aae78e5146e13d.jpeg

Spring Boot JVM ↑↑↑
37abccbd0961a85b8c7b1c3b9a2aea22.jpeg
Quarkus JVM ↑↑↑
a3ea94a8e938060777087312dd09c4fe.jpeg
Spring Boot native ↑↑↑
a1dd750e06565d9f35bbc3669eb61869.jpeg
Quarkus native ↑↑↑

Although Quarkus had higher peaks during the test, it did consume less memory resources.

Response time

Finally, Spring Boot seems to have a slight advantage when it comes to response time and peak number of threads used. It is able to handle the same load using fewer threads while also having better response times.

The Spring Boot Native version shows better performance in this case. But let’s look at the response time distribution for each version:

44edb69a28fe660cf42f8e6a1b7ab138.jpeg

Spring Boot JVM ↑↑↑

Although there are more outliers, the Spring Boot JVM version has made the best progress over time, most likely due to JIT compiler optimizations[1].

5ebc5f05a9e878ba8850bb22c5656a17.jpeg

Quarkus JVM ↑↑↑
7a5bb7a50152d34fb3296792423d3b3d.jpeg
Spring Boot native ↑↑↑
74de9f18efec4f5396d57fcf427645f9.jpeg
Quarkus native ↑↑↑

Quarkus shows strength in low resource utilization. However, at least in this experiment, Spring Boot was on par with Quarkus in terms of throughput and responsiveness.

Both frameworks are able to handle all requests without any errors. Not only that, their performance is also very similar, there is not much difference.

In summary

All things considered, both frameworks are great choices when implementing Java applications.

Native programs are fast and have low resource consumption, making them an excellent choice for serverless, short-living applications and resource consumption-sensitive environments.

JVM applications, on the other hand, appear to have more overhead, but have excellent stability and high throughput over time, making them ideal for robust, long-lived applications.

The code for the test programs and the scripts used to test them can be found on GitHub [2].

6Convert from Spring to Quarkus

With the rise of K8s, the Quarkus framework, which has good support for native applications, has attracted more and more attention. Many developers are considering switching from Spring to Quarkus. However, developers often have to put their existing knowledge on hold when they begin to evaluate a new framework.

Fortunately, Quarkus is different, as it was created by a team of engineers with deep expertise in Java technology. This includes Spring API compatibility, as the engineers who created Quarkus were also the same engineers who powered Spring Boot on the Red Hat Runtime.

7I am a Spring developer, why should I choose Quarkus?

It is becoming increasingly clear that containerization, and Kubernetes in particular, is forcing a re-evaluation of Java for developing cloud-native applications. Kubernetes is a highly dynamic shared infrastructure. Infrastructure investments become more cost-effective as the number of applications hosted in the cluster increases and the ability to respond to application lifecycle changes (such as redeployment and scaling up/down) increases.

Traditional Java cloud-native runtimes add new layers on top of the existing stack without truly rethinking the underlying layers. This results in greater memory consumption and slower startup times, so much so that many companies are now willing to give up their deep Java expertise and retrain talent for Go and Node.js in order to get more value from their heavy investments in Kubernetes clusters. and development tools.

0c7a68c90bb09e6f4206f09dfd267b2d.jpeg

Traditional cloud-native Java stack ↑↑↑

This is exactly the problem Quarkus solves. Quarkus is optimized for memory usage and fast startup times. Compared to other cloud-native Java stacks, Quarkus applications running on the JVM can deliver nearly twice as many application instances in the same amount of RAM, and up to 7x more instances when packaged as native binaries.

This goes beyond simply compiling to native binaries using SubstrateVM[3] (a feature of GraalVM).

Quarkus optimizes the traditional “highly dynamic” framework specifically for Kubernetes infrastructure, resulting in lower memory utilization and faster initial startup, resulting in significant improvements in runtime efficiency. These optimized and well-documented frameworks, called extensions, are composed of best-of-breed standard APIs.

3119752ede8edbc5b6c7589c42d3182e.jpeg

Runtime efficiency ↑↑↑
9f101817f1b5b66b4eea35c71a0378d4.jpeg
Quarkus stack ↑↑↑

Why should our company migrate from Spring Boot to Quarkus?

Taking our company as an example, our old system is based on Spring and Tomcat. This traditional framework gave us some trouble when we maintained and deployed it, and we decided to migrate to Quarkus for the following reasons:

  • Memory and CPU consumption: For the operations being performed, the Spring and Tomcat frameworks are using too many resources outside of the main purpose of the application.

  • Warm-up time: A Spring application may take 10-20 seconds to start before the application can begin to warm up.

  • Useless code: As developers, we all hate boilerplate code.

  • Testing: Quarkus makes writing unit and integration tests very easy. Just put a @QuarkusTest annotation there and it will actually launch the entire application to run your tests.

  • Scale-out vs. Scale-up: The smaller each application is (resource-wise), the more we can add. Horizontal scalability wins here.

  • Learning Curve: Quarkus’ online documentation is very simple and easy to understand.

8What existing knowledge can Spring developers leverage?

Quarkus’ Spring API compatibility includes Spring DI, Spring Web, and Spring Data JPA. Other Spring APIs such as Spring Security and Spring Config are also planned. When running on the JVM, Quarkus applications can leverage virtually any Java library. As long as Java reflection is not used, these Java libraries can be compiled natively.

For example, the Lombok library, popular among Spring developers, compiles natively. To be clear, Spring API compatibility in Quarkus is not intended as a complete Spring platform for rehosting existing Spring applications. The goal is to make developing new applications based on Quarkus a natural onboarding experience. Combined with pre-optimizedextensions, Quarkus provides a wealth of functionality for microservices development. Many developers have successfully migrated Spring applications to Quarkus.

Spring framework is highly dynamic in nature. To solve this problem, Quarkus’ Spring Compatibility extension maps Spring APIs to APIs in existing extensions that are already optimized for fast startup, reduced memory utilization, and native compilation, such as RestEasy and CDI. Additionally, Quarkus’ Spring compatibility extension does not use Spring application context. For these reasons, trying to use additional Spring libraries may not work.

Quarkus Spring Web Example

import java.util.List;
import java.util.Optional;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/person")
public class PersonController {
    @GetMapping(path = "/greet/{id}", produces = "text/plain")
    public String greetPerson(@PathVariable(name = "id") long id) {
        String name="";
        // ...
        return name;
    }

    @GetMapping(produces = "application/json")
    public Iterable<Person> findAll() {
        return personRepository.findAll();
    }

Quarkus Spring Repository Example

package org.acme.springmp;

import java.util.List;
import org.springframework.data.repository.CrudRepository;

public interface PersonRepository extends CrudRepository<Person, Long> {
    List<Person> findByAge(int age);
}

Quarkus Spring Service + MicroProfile Fault Tolerance Example

import org.eclipse.microprofile.faulttolerance.Fallback;
import org.eclipse.microprofile.faulttolerance.Timeout;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service // Spring
public class PersonService {

    @Autowired // Spring
    @RestClient //MicroProfile
    SalutationMicroProfileRestClient salutationRestClient;

    @Value("${fallbackSalutation}") // Spring
    String fallbackSalutation;

    @CircuitBreaker(delay=5000, failureRatio=.5) // MicroProfile
    @Fallback(fallbackMethod = "salutationFallback")//MicroProfile
    public String getSalutation() {
        return salutationRestClient.getSalutation();
    }

Does

9 have additional benefits for Spring developers?

In addition to improving memory utilization and startup time, Quarkus provides Spring developers with the following benefits:

  • Function as a Service (FaaS) . When compiled to native binaries, Quarkus applications can start in 0.0015 seconds, allowing you to use existing Spring and Java API knowledge with FaaS capabilities. (Azure, AWS Lambda)

  • Live encoding. Start with a “Hello World” sample application and convert it into complex microservices without restarting the application. Just save and reload your browser to see changes along the way. Quarkus live coding works “out of the box” and is IDE agnostic.

  • Supports reactive and imperative models. Quarkus has a reactive core that supports traditional imperative models, reactive models, or both in the same application.

  • Early detection of dependency injection errors. Quarkus catches dependency injection errors during compilation rather than at runtime.

  • A combination of the best frameworks and standards. Quarkus supports Spring API compatibility, Eclipse Vert.x, MicroProfile (JAX-RS, CDI, etc.), reactive streaming, messaging, and more in the same application, allowing you to use both Spring and MicroProfile APIs in a single project.

10How can Spring developers start learning Quarkus?

Recommended steps include:

  • See the Getting Started Guide [4] for a general introduction to Quarkus.

  • See the guides for Spring DI[5], Spring Web[6], and Spring Data JPA[7].

  • Create a new application using code.quarkus.io[8].

11 reference link

www.baeldung.com/spring-boot…[9]

quarkus.io/blog/quarku…[10]

www.logicmonitor.com/blog/quarku…[11]

Reference materials

[1]

JIT compiler optimization: https://docs.oracle.com/cd/E13150_01/jrockit_jvm/jrockit/geninfo/diagnos/underst_jit.html

[2]

Found on GitHub: https://github.com/eugenp/tutorials/tree/master/quarkus-vs-springboot

[3]

SubstrateVM: https://github.com/oracle/graal/tree/master/substratevm

[4]

Getting started: https://quarkus.io/get-started/

[5]

Spring DI: https://quarkus.io/guides/spring-di-guide

[6]

Spring Web: https://quarkus.io/guides/spring-web-guide

[7]

Spring Data JPA: https://quarkus.io/guides/spring-data-jpa-guide

[8]

code.quarkus.io: https://code.quarkus.io/

[9]

https://www.baeldung.com/spring-boot-vs-quarkus: https://www.baeldung.com/spring-boot-vs-quarkus

[10]

https://quarkus.io/blog/quarkus-for-spring-developers/: https://quarkus.io/blog/quarkus-for-spring-developers/

[11]

https://www.logicmonitor.com/blog/quarkus-vs-spring: https://www.logicmonitor.com/blog/quarkus-vs-spring

Source: juejin.cn/post/7023317351563001886




BoutiqueRecommended

1. Please stop deploying jar packages manually, it’s too low! Dynamic upload hot deployment is really cool!
2.Lua + Redis + SpringBoot = awesome!
3. A set of universal asynchronous processing solutions
4. Sorry, Nginx should be abandoned!
5. Microsoft fully embraces Java!
6. SpringBoot + Disruptor achieves extremely fast and high-concurrency processing, supporting 6 million orders per second without any pressure!
7. The company has been using the multi-account unified login solution for more than 3 years. It is universal and stable!
8. Maxed out, Spring Event business decoupling artifact!