找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3720

积分

0

好友

492

主题
发表于 昨天 23:47 | 查看: 13| 回复: 0

来源:juejin.cn

1 概述

对于Java开发者来说,Spring Boot几乎已经成了微服务的代名词,而Quarkus这个名字或许还略显陌生。不过,Quarkus官网那句“Supersonic Subatomic Java”的标语,确实让人眼前一亮。

Quarkus 是专为 OpenJDK HotSpot 和 GraalVM 设计的 Kubernetes Native Java 框架,基于业界顶尖的 Java 库和标准构建。它的出现,为开发 Linux 容器以及 Kubernetes 原生 Java 微服务开辟了一条全新的路径。

在本文中,我们将对 Spring Boot 和 Quarkus 这两个 Java 框架进行一个相对务实的对比,从而更直观地了解它们的异同,以及各自的亮点。同时,我们还会通过一系列性能测试来衡量它们的真实表现。最后,会介绍如何从 Spring 顺利过渡到 Quarkus。

2 SpringBoot

Spring Boot 是一个基于 Java 的企业级应用框架。它让所有 Spring 项目都能轻松上手,还集成了大量开箱即用的功能,帮助开发人员大幅提升效率。

Spring Boot 大幅减少了配置和样板代码的数量。得益于其约定优于配置的核心理念,它能根据依赖自动配置默认项,极大地缩短了 Java 应用程序的开发周期。

3 Quarkus

Quarkus 采用了与 Spring Boot 类似的设计思路,但它额外带来了一个关键优势:以更快的启动时间、更低的资源消耗和更高的运行效率,交付更小的工件(即所谓的超音速、亚原子)。

它专为云、无服务器和容器化环境而生。尽管侧重点略有不同,Quarkus 同样能与主流的 Java 框架顺畅集成。

4 比较

两者都能很好地与其他项目和框架集成,但内部实现和架构却截然不同。例如,Spring Boot 提供了两种 Web 编程模型:阻塞式(Servlets)与非阻塞式(WebFlux)

Quarkus 也提供了这两种方式,但不同点在于,它允许同时使用阻塞和非阻塞方法,并且在其架构中深度嵌入了响应式编程模式。

为了得到更客观的数据,我们两个测试应用都采用完全响应式的实现,分别用 Spring WebFlux 和 Quarkus 的响应式特性来完成。

此外,Quarkus 的一个重要杀手锏是能够创建原生镜像(Native Images,即针对特定平台的可执行二进制文件)。因此,我们在对比中也加入了两个框架的原生镜像版本。需要说明的是,Spring 的原生镜像支持目前仍处于试验阶段,并且整个过程都需要依赖 GraalVM。

测试应用

我们设计的应用程序提供三个 API:一个用于创建邮政编码,一个用于按编码查询详情,最后一个用于按城市查询。这些 API 都通过前面提到的响应式方式实现,数据库则选用 PostgreSQL。

我们想做一个比“HelloWorld”稍微复杂一点的示例,毕竟数据库驱动和序列化框架等环节都会影响最终的对比结果。当然,大部分实际应用都离不开这些处理。

所以,这次对比的目的并非要证明“谁更好或谁更高效”,而是基于这个特定实现,进行一次有针对性的案例分析。

测试计划

我们将使用 JMeter 执行压力测试并分析其报告,同时借助 VisualVM 实时监控应用的资源占用情况。

测试将持续 5 分钟,覆盖所有 API。从预热开始,然后逐步增加并发用户,直到达到 1,500 个。测试初期,我们先填充数据库,随后再发起查询,过程如下:

JMeter预期并行用户数曲线图

JMeter线程组参数配置

所有测试均运行在以下配置的机器上:

性能测试环境Fedora 34配置

由于测试环境没有与其他后台进程完全隔离,结果可能并非绝对精准。但如前所述,我们无意对两个框架的性能进行过于宽泛和精细的剖析。

5 调查结果

对开发人员而言,两个框架的开发体验都很棒。不过,Spring Boot 拥有更完善的文档和更丰富的在线资料。Quarkus 在这方面的追赶速度很快,但仍略逊一筹。

在具体指标上,我们得到了以下数据:

Spring Boot与Quarkus性能指标对比表

通过这个实验,我们可以直观地看到:在 JVM 和原生版本中,Quarkus 的启动时间几乎都只有 Spring Boot 的一半。构建时间也短得多。其中,原生镜像的构建耗时:9 分钟(Quarkus)对 13 分钟(Spring Boot);JVM 版本构建耗时:20 秒(Quarkus)对 39 秒(Spring Boot)。

在工件(Artifact)大小上,Quarkus 同样以更小的体积领先。原生镜像:75MB (Quarkus) 对 109MB (Spring Boot);JVM 版本:4KB (Quarkus) 对 26MB (Spring Boot)。

至于其他指标,结论就没那么一目了然了,需要再深入分析一下。

CPU

在预热阶段初期,JVM 版本的 CPU 消耗明显更高。之后,CPU 使用率逐渐平稳,所有版本的消耗变得相对均衡。

以下是 JVM 和 Native 版本下 Quarkus 的 CPU 消耗情况:

Quarkus JVM版本CPU使用率监控图
JVM 版的 Quarkus CPU 消耗情况

Quarkus原生版本CPU使用率监控图
Native 版的 Quarkus CPU 消耗情况

内存

内存情况更复杂一些。首先,很明显,两个框架的 JVM 版本都为堆内存预留了更多空间。不过,Quarkus 从一开始就保留了更少的内存,启动过程中的内存占用也更低。

在测试期间观察内存利用率,可以发现 Native 版本似乎不像 JVM 版本那样频繁或高效地回收内存。通过调整相关参数(比如 GC 策略),这种情况可以改善。但在本次对比中,我们一概采用默认设置,没有修改任何 JVM 选项或参数。

下面是各版本的内存使用对比图:

Spring Boot JVM内存使用图
Spring Boot JVM 内存使用

Quarkus JVM内存使用图
Quarkus JVM 内存使用

Spring Boot原生内存使用图
Spring Boot 原生内存使用

Quarkus原生内存使用图
Quarkus 原生内存使用

在测试期间,尽管 Quarkus 偶尔出现更高的峰值,但总体内存资源消耗确实更低。

响应时间

最后来看响应时间与峰值线程数,Spring Boot 看似略占上风。它能用更少的线程承受相同的负载,同时交出更好的响应时间。

在其中,Spring Boot 原生版本表现最为出色。先看各版本的响应时间分布:

Spring Boot JVM响应时间分布图
Spring Boot JVM 响应时间分布

虽然有较多异常值,但随着时间推进,Spring Boot JVM 版本展现出了最佳的长跑能力,这主要归功于 JIT 编译器优化

Quarkus JVM响应时间分布图
Quarkus JVM 响应时间分布

Spring Boot原生响应时间分布图
Spring Boot 原生响应时间分布

Quarkus原生响应时间分布图
Quarkus 原生响应时间分布

Quarkus 在低资源占用上展现了硬实力,但在本次实验中,Spring Boot 在吞吐量和响应能力上与 Quarkus 不相上下。

两个框架都处理了所有请求,没有出现任何错误。而且,它们的整体表现十分接近,并没有拉开明显差距。

总而言之

综合来看,在实现 Java 应用时,这两个框架都是非常优秀的选择。

Native 程序启动快、资源消耗低,很适合无服务器、短期运行的应用,以及对资源敏感的场景。

JVM 应用虽然开销稍大,但具备出色的长期稳定性和高吞吐量,更适合健壮、长期运行的应用。

本次测试的代码以及压测脚本均已公开在 GitHub 上。

6 从 Spring 转换到 Quarkus

随着 Kubernetes 的兴起,对原生应用支持良好的 Quarkus 自然吸引了越来越多开发者的目光,不少人开始考虑从 Spring 迁移过去。然而,通常评估新框架时,开发者不得不暂时搁置已有的知识积累。

好在 Quarkus 是个例外,因为它是由一群深耕 Java 技术的工程师打造的。这包括对 Spring API 的兼容性设计——创建 Quarkus 的工程师,正是那些在 Red Hat 运行时上为 Spring Boot 提供支持的同一批人。

7 我是 Spring 开发者,为什么要选Quarkus?

越来越明显,容器化,尤其是 Kubernetes,正在深刻重塑 Java 开发云原生应用的方式。Kubernetes 提供了高度动态的共享基础设施,随着集群中应用数量的增长,以及应用对生命周期变化(如重新部署、弹性伸缩)的快速响应,基础设施的投入会变得更加划算。

传统 Java 云原生运行时只是在现有技术栈上叠加新层,而没有真正反思底层。这导致了更严重的内存占用和更慢的启动速度。以至于不少公司为了从 Kubernetes 集群的大规模投入中获取更多价值,宁愿放弃深厚的 Java 知识,选择用 Go 或 Node.js 重新培训团队。

传统云原生Java技术栈架构
传统云原生 Java 栈

这正是 Quarkus 要解决的问题。Quarkus 对内存利用率和启动时间进行了深度优化。相比其他云原生 Java 栈,在 JVM 上运行的 Quarkus 能在相同内存中容纳近两倍的实例;当编译为原生二进制时,实例数可以增加到 7 倍。

这不仅仅是简单地利用 SubstrateVM(GraalVM 的一项特性)编译成原生二进制。

Quarkus 针对 Kubernetes 环境对传统“高度动态”的框架进行了底层优化,降低了内存占用,并加快了初始启动速度,最终实现了运行效率的显著跃升。这些经过深度优化且文档齐全的框架被称为“扩展”,由业界最佳的标准化 API 组成。

Quarkus与Spring Boot内存和启动时间对比
运行时效率对比

Quarkus技术栈架构
Quarkus 栈

我司为什么要从 Spring Boot 迁移到 Quarkus?

以我们公司为例,老系统基于 Spring 和 Tomcat。在维护与部署过程中,这个传统框架带来了一些麻烦,促使我们决定迁移到 Quarkus:

  • 内存和 CPU 消耗:Spring 和 Tomcat 在应用核心功能之外,占用了过多资源。
  • 预热时间:Spring 应用可能需要 10~20 秒来启动,之后才能进入预热阶段。
  • 无用代码:作为开发者,谁不讨厌样板代码呢?
  • 测试:Quarkus 让单元测试和集成测试变得异常简单。只需打一个 @QuarkusTest 注解,它实际上会启动整个应用来运行你的测试。
  • 横向扩展 vs. 纵向扩展:每个应用占用的资源越小,我们能部署的实例就越多。这里,横向扩展完胜。
  • 学习曲线:Quarkus 的在线文档非常简洁易懂。

8 Spring 开发者可以活用哪些现有知识?

Quarkus 的 Spring API 兼容性涵盖了 Spring DI、Spring Web 和 Spring Data JPA。同时,Spring Security 和 Spring Config 等接口也在规划中。在 JVM 上运行时,Quarkus 应用几乎可以调用任何 Java 库。只要不依赖 Java 反射,这些库就可以被编译为原生代码。

例如,深受 Spring 开发者喜爱的 Lombok 库,就能被原生编译。需要明确的是,Quarkus 对 Spring API 的兼容,并非为了作为一个完整的 Spring 平台去直接承载旧的 Spring 应用。它的目的是让基于 Quarkus 开发新应用成为一种自然的入门体验。结合预先优化好的扩展,Quarkus 为微服务开发提供了丰富的能力。很多开发者已经成功将 Spring 应用迁移到了 Quarkus。

Spring 框架本质上是高度动态的。为了解决这个问题,Quarkus 的 Spring 兼容性扩展将 Spring API 映射到已有的、已针对快速启动、低内存占用和原生编译进行优化的扩展上,例如 RestEasy 和 CDI。此外,Quarkus 的 Spring 兼容性扩展并不使用 Spring 应用上下文。基于这些原因,如果你试图引入额外的 Spring 库,可能无法正常工作。

Quarkus Spring Web 示例

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 示例

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 示例

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();
    }
}

9 对Spring开发者有额外的好处吗?

除了内存利用率和启动时间的提升,Quarkus 还为 Spring 开发者带来了以下实实在在的好处:

  • 功能即服务 (FaaS)。编译为原生二进制后,Quarkus 应用能在 0.0015 秒内启动,这意味着你现有的 Spring 和 Java API 知识可以直接用在 FaaS 场景中(如 Azure、AWS Lambda 等)。
  • 实时编码。从一个“Hello World”示例开始,逐步将它改造成复杂的微服务,完全不需要重启应用。只需保存代码,刷新浏览器就能看到变化。Quarkus 的实时编码功能“开箱即用”,与 IDE 无关。
  • 支持反应式和命令式模型。Quarkus 拥有反应式内核,但既能支持传统的命令式模型,也能拥抱反应式模型,甚至在同一应用中同时混用两者。
  • 早期检测依赖注入错误。Quarkus 在编译阶段,而不是运行时,就会捕获依赖注入错误,帮你提前排雷。
  • 最佳框架和标准的结合。Quarkus 在同一个应用中支持 Spring API 兼容性、Eclipse Vert.x、MicroProfile(JAX-RS、CDI 等)、反应式流与消息传递,你完全可以在一个项目里同时使用 Spring 和 MicroProfile API。

10 Spring开发者如何开始学习Quarkus?

推荐的入门路线如下:

  • 阅读入门指南,对 Quarkus 形成一个整体认识。
  • 分别浏览 Spring DISpring WebSpring Data JPA 的专项指南。
  • 使用 code.quarkus.io 创建一个全新的 Quarkus 项目。

本文由云栈社区编译分析,更多技术干货欢迎访问云栈社区。




上一篇:Lock4j:基于Spring AOP的分布式锁框架,支持Redis与Zookeeper
下一篇:项目经理的“治未病”困境:为什么越优秀越难晋升?
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-6-15 18:05 , Processed in 0.617577 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表