-
背景
随着Docker、kubernetes技术的发展,云原生技术已然成为了未来技术发展的方向。提及云原生技术,首先想到的就是容器化、微服务、服务网格等,这些是必要元素,不代表着这些元素就是云原生。云原生的目的是提高开发效率,提升业务的敏捷度、可用性、资源利用率,降低开发成本。而传统的Java框架(Spring)设计之初并未为云原生预留拓展的空间,导致了Spring应用启动慢、内存使用高在云原生世代并大家诟病。当然,也不存在其他的云原生的技术体系(技术、运维、部署等方面)。因此,如何让Java应用更加完美的转化到Kubernetes层面是一个很大的难题,Java技术需要一个变革去适应新的挑战。
-
变革
为了应带挑战,Java也在积极的变化,一些轻量级的云原生微服务框架应运而生。其中,以2018年RedHat推出的专为云原生设计的Java开发框架(让Java开发者能为现在的云原生世代创建应用程序) - Quarkus最为出名。设计之初便与Kubernetes紧密结合,以提高构建微服务、无服务以及基于云服务的应用程序的开发效率为目的。
-
概述
Quarkus是一个专为Java虚拟机(GraalVM和HotSpot)和本地编译(可以执行提前编译(AOT),将字节码转换为本地机器码,代码变为二进制)而设计的全栈式Kubernetes-native Java框架,用于专门针对容器环境优化Java应用,并使其成为Severless、Cloud、Kubernetes环境的高效开发平台。Quarkus可以与常用的Java标准、框架、库协同工作,例如可以和Spring、Kafka、Camel、Hibernate等集成在一起使用。
- 背景知识
- GraalVM
- 简介 - 由Oracle公司开发,是基于HotSpot上增强的一个跨语言的全栈虚拟机。全栈虚拟机是指他可以作为大部分语言的运行平台(包括Java、Scala、Groovy、Kotlin、JavaScript、Ruby、Python、C、C++等)。还可以编译多语言混合开发的程序(不同的语言中混用其它语言的接口或方法等)。针对Java而言的话,由于它是基于HotSpot开发的,天生就可以作为一套完全符合Java标准的虚拟机来使用。差异只在即时编译器上:将HotSpot内置的C2换成了Graal。
- 优势 - 将Java编译为可执行二进制文件(Java byte code -> machine code) > HotSpot 80% C++
- Native Image
- Native Image是一种将Java代码提前编译为独立可执行文件的技术,此刻执行文件包括应用程序类、依赖、运行时库以及JDK静态连接的本机代码。Graalvm通过子模块SubstrateVM来支持Native Image,相比JVM其生成的程序具有更快的启动时间和更低的运行时开销。
- JVM 在"预热" JIT 活动期间使用了大量 CPU,而原生可执行程序几乎不使用 CPU,因为所有昂贵的编译操作都发生在构建时。
- GraalVM Native Image
- GraalVM 可以为基于 JVM 的程序创建本地镜像。 镜像生成过程中,通过使用静态分析技术,从 Java main 方法开始,查找所有可以执行到的代码,然后执行全量的提前编译(AOT,ahead-of-time)。生成的二进制可执行文件,包含整个程序的所有机器码指令,可以快速启动和执行,还可以被其他程序链接。编译时还可以选择包含 GraalVM 编译器,以提供额外的即时(JIT)编译支持,从而高性能地运行任何基于 GraalVM 的语言。
- GraalVM
-
定义
A Kubernetes Native Java stack tailored for OpenJDK HotSpot and GraalVM, crafted from the best of breed Java libraries and standards.
-
架构
-
特性
* 应用启动、停止非常快,包括第一次请求响应时间
* 内存占用低
* 可以使用GraalVM编译成本地代码
* 支持命令式、响应式以及混合模式
* Java库和标准规范同类“最佳”
* 简易统一的配置管理 -
优势
* 专为开发者设计
Quarkus 的设计从一开始就立足于简单易用,其功能几乎不需要配置即可正常使用。开发人员可以为其应用选择所需的Java框架,而这些应用可以在JVM模式下运行,也可以在原生模式下进行编译和运行。为了方便开发人员的工作,Quarkus 还包含以下功能:
* 实时编码,旨在让开发人员能够即时检查代码更改的影响并快速进行故障排除
* 带有嵌入式托管事件总线的统一命令式和响应式编程
* 统一配置
* 生成简单的原生可执行文件
* 远程开发
* Kubernetes 原生
Quarkus 提供了零门槛的Kubernetes构建,可以轻松部署应用程序,而无需了解平台的所有复杂性。Quarkus 允许开发人员自动生成 Kubernetes 资源,包括构建和部署容器镜像,而无需手动创建 YAML 文件。这其中,包括以下的特性:
* 一键部署
* 应用配置
* 健康检查
* 追踪调试
* 容器优先
无论是将应用托管在公有云上还是内部托管的 Kubernetes 集群中,快速启动和低内存消耗等特性对于降低总体主机成本来说都至关重要。Quarkus 的开发遵从了容器优先的原则,这意味着它已通过以下方式针对降低内存使用和加快启动时间进行了优化:
* 鼎力支持 Graal/SubstrateVM
* 构建时元数据处理 - 所有的类在构建时就已经完成了初始化
* 减少反射的使用 - 默认情况下,未被直接调用的类、方法、字段都会被移除(构建时手动注册反射类、方法;代理类)
* 本机镜像预启动
因此,Quarkus 构建的应用其内存消耗只有传统 Java 的 1/10,而且启动时间更快(快了 300 倍),这些都大大降低了云资源的成本。
* 提供高品质的类库和标准
提供了数百个开箱即用的同品类最佳的依赖库,例如:CDI、JPA、JAX-RS、Camel等
* 支持命令式和响应式的编程
![icon-reactive.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b299b004e2174c85bceda6aa0e5fbdb5~tplv-k3u1fbpfcp-zoom-1.image)
在设计上,Quarkus 能够在开发应用时无缝地结合熟悉的命令式代码和非阻塞、响应式样式。这对于习惯使用命令式模型而不想切换风格的 Java 开发人员以及使用云原生/响应式方法的开发人员都非常有用。Quarkus 开发模型可以适应正在开发的任何应用。对于在新的无服务器架构、微服务、容器、Kubernetes、功能即服务(FaaS)和云环境中运行 Java 而言,Quarkus 堪称是一个有效的解决方案,因为在创建时就充分考虑了所有这些因素。
-
能力
遵循Microprofile规范
* 可观察性
* OpenTelemetry
* OpenTracing
* MicroMeter
* Sentry
* 容错性
* 重试
* 超时
* 降级
* 熔断
* 认证、鉴权
* 消息
* 响应式编程
* 配置
* 日志
-
入门
- 在线编码
@Path("/hello") public class GreetingsResource { @GET @Produces(MediaType.TEXT_PLAIN) public String hello() { return "Hello RESTEasy"; } }
- 构建本地可执行文件
native native
- 持续测试
2022-09-11 14:21:34,338 ERROR [io.qua.test] (Test runner thread) Test GreetingResourceTest#testHelloEndpoint() failed : java.lang.AssertionError: 1 expectation failed. Response body doesn't match expectation. Expected: is "hello" Actual: hello world at io.restassured.internal.ValidatableResponseImpl.body(ValidatableResponseImpl.groovy) at org.acme.getting.started.GreetingResourceTest.testHelloEndpoint(GreetingResourceTest.java:21) -- Test run failed, 2 tests were run, 1 failed, 0 were skipped. Tests took 295ms Press [r] to re-run, [v] to view full results, [p] to pause, [h] for more options>
- 创建REST服务
@Path("/fruits") public class FruitResource { @POST public Set add(Fruit fruit) { fruits.add(fruit); return fruits; } }
- 响应式编程
@Path("/fruits") @ApplicationScoped public class FruitResource { @POST public Uni create(Fruit fruit) { return Panache.withTransaction(fruit::persist) .onItem().transform(inserted -> Response.created(URI.create("/fruits/" + inserted.id)).build()); } }
- Kubernetes 部署
{ { "apiVersion" : "apps/v1", "kind" : "Deployment", "metadata" : { "annotations": { "app.quarkus.io/vcs-url" : "", "app.quarkus.io/commit-id" : "", }, "labels" : { "app.kubernetes.io/name" : "test-quarkus-app", "app.kubernetes.io/version" : "1.0.0-SNAPSHOT", }, "name" : "test-quarkus-app" }, "spec" : { "replicas" : 1, "selector" : { "matchLabels" : { "app.kubernetes.io/name" : "test-quarkus-app", "app.kubernetes.io/version" : "1.0.0-SNAPSHOT", } }, "template" : { "metadata" : { "labels" : { "app.kubernetes.io/name" : "test-quarkus-app", "app.kubernetes.io/version" : "1.0.0-SNAPSHOT" } }, "spec" : { "containers" : [ { "env" : [ { "name" : "KUBERNETES_NAMESPACE", "valueFrom" : { "fieldRef" : { "fieldPath" : "metadata.namespace" } } } ], "image" : "yourDockerUsername/test-quarkus-app:1.0.0-SNAPSHOT", "imagePullPolicy" : "Always", "name" : "test-quarkus-app" } ] } } } }, { "apiVersion" : "v1", "kind" : "Service", "metadata" : { "annotations": { "app.quarkus.io/vcs-url" : "", "app.quarkus.io/commit-id" : "", }, "labels" : { "app.kubernetes.io/name" : "test-quarkus-app", "app.kubernetes.io/version" : "1.0.0-SNAPSHOT", }, "name" : "test-quarkus-app" }, "spec" : { "ports" : [ { "name" : "http", "port" : 8080, "targetPort" : 8080 } ], "selector" : { "app.kubernetes.io/name" : "test-quarkus-app", "app.kubernetes.io/version" : "1.0.0-SNAPSHOT" }, "type" : "ClusterIP" } } }
-
对比
对比项 | Quarkus | Spring Boot |
---|---|---|
依赖管理 | CDI | DI |
微服务开发 | Microprofile API | Spring自带开发微服务模块 |
数据持久化 | 提供例如Hibernate、Panache的依赖库 | Spring Data |
启动速度 | 非常快 | 慢 |
前端开发 | 除了基础的前端开发外,还有拓展 | SpringMVC 和 ThymeLeaf |
成熟度 | 中 | 高 |
响应式应用 | Mutiny | Mono & Flux |
Kubernetes资源生成 | 支持 | 不支持 |
GraalVM Native Image | 支持 | 实验 |
应用监控 | MicroProfile | Spring Boot Actuator |
服务治理 | Micro Profile | Netflix OSS |
-
实验
- 性能展示
- 研发成本
- Spring Boot - 大型复杂的企业应用
- Quarkus - CNA
- 对比实验
- 启动、构建时间
对比 | Spring Boot | Quarkus JVM | Quarkus Native |
---|---|---|---|
启动时间 | ~ 10s | ~3s | < 0.1s |
构建时间 | ~18s | ~20s | ~4m Local GraalVM ~6m Multistaging |
- JAR 文件大小
Spring Boot | Quarkus |
---|---|
58.9M | 30M |
- Docker Iamge
Spring Boot | Quarkus | Quarkus Native |
---|---|---|
237M | 213M | 68.2M |
- 运行时 - 8CPU/8GB
- RAM
Spring Boot | Quarkus JVM | Quarkus Native |
---|---|---|
386.5M | 205.7M | 15.96M |
- CPU
Spring Boot | Quarkus JVM | Quarkus Native |
---|---|---|
0.38% | 0.32% | 0.02% |
- 重启请求响应 - 每秒一次请求,请求100s
Spring Boot | Quarkus JVM | Quarkus Native |
---|---|---|
19 | 7 | 0 |
- 对比结论(8CPU/8GB)
对比维度 | Spring Boot | Quarkus JVM | Quarkus Native |
---|---|---|---|
启动时间 | ~10s | ~2s | < 0.1s |
构建时间 | ~18s | ~20s | ~4m Local GraalVM ~6m Multistaging |
镜像大小 | 237M | 213M | 68.2M |
RAM | 386.5M | 205.7M | 15.96M |
CPU | 0.38% | 0.32% | 0.02% |
单实例失败次数重启/60s 1请求/s | 19 | 7 | 0 |
单实例失败次数重启/20s 1请求/s | 99 | 37 | 1 |
K8s 单副本 成功率Kill/15s 请求/0.5s/ | 51.2% | 88.4% | 96.7% |
-
性能对比总结 - 启动时间、资源消耗、安全性优势明显(开发CNA 应用的首选)
-
思考
-
Quarkus为我们带来了什么?
- 更快的启动速度
- 更少的内存消耗
- 降低了资源成本
- 提高了Java应用的性能
- 提高了开发人员的生产效率
-
使用了Quarkus我们的应用就会启动更快、内存占用更少了吗?
- CNA - Cloud Native Application - 例如,启动时有大量的业务初始化(耗时、耗能)
- 本地编译的局限性
- 反射
- 类热加载
-
实践
-
落地步骤
- 升级JDK版本(8->11/17)
- Spring Boot -> Quarkus JVM
- Quarkus JVM -> Native
-
试点业务
- 优先选择轻量级的业务
- 外部依赖组件较少的
- 通信协议较为单一的
- 优先选择轻量级的业务
-
推荐业务
- Scheduler
- Storage
-
总结
Quarkus为Java带来了云原生(Kubernetes-Native Java)的技术解决方案,并实现了以前无法实现的场景,尤其是在应用启动时间、资源消耗方面。Quarkus在跟进技术发展时,提供了简易的框架(新事物的竞争力之一,高起点、没有历史包袱)并将它们进一步发展(不是试图重塑业界熟悉的技术,而是优化它们)。这使得开发人员在使用该框架之前,已经熟悉了这些技术标准,可以迅速的上手,例如CDI,JAX-RS。同时,Quarkus的AOT和JVM的优化为应用程序带来了更好的性能体验。在业务方案层面,因其解决方案完备、资源消耗少、设计之初就围绕了云原生应用,其优势和潜力,相对传统的解决方案,高下立判。Quarkus被誉为下一代Java框架的王位继承人!当前Quarkus社区已经发布了2.12.2版本,其稳定性、活跃度(star>10K)已经满足了生产需求。是时候拥抱新的变革了!