作者 | Jason Greene, John Clingan, Eric Deandrea
译者 | 平川
策划 | 丁晓昀
Java 在企业应用中占据主导地位。但在云上,Java 比一些竞争对手的使用成本更高。原生编译降低了在云中使用 Java 的成本:它创建的应用程序启动速度更快,占用的内存更少。
原生编译给 Java 用户带来了许多问题:原生 Java 对开发有什么影响?我们什么时候应该改用原生 Java?什么时候不应该?我们应该使用什么框架来开发原生 Java?这个系列将回答这些问题。
1 Kubernetes Native Java 是什么?
“[它们] 可以运行 3 倍密度的部署,而又不影响服务的可用性和响应时间。”—— Lufthansa Technik
采用 Kubernetes,组织可以更快、更经济有效地交付业务价值。为什么?因为 Kubernetes 简化了一些原本困难的任务:它会在负载增加时自动实现应用程序的水平扩展,并能执行滚动升级。

但是,这种方法与 Java 传统的三层架构模型(大堆、垂直扩展、长时间运行的进程)相冲突。
历史上,Java 通过分配和最大化持有的系统资源,来适应随时间变化的负载,实现卓越的性能。
开发人员非常喜欢长时间运行的、重量级的、JVM 高度动态的 Java 模型,以便在运行多个不同的应用程序时可以支持丰富的声明式编程模型。延迟扫描和运行时类路径分析给我们带来了控制反转,减少了样板代码——以启动时间和内存占用为代价。这些缺点在堆很大的大型单体应用程序中并没有太大影响,因为启动代价只需要支付一次。
但是,在 Kubernetes 中运行这些传统的 Java 应用程序会导致许多胖 JVM,它们承载着需要大量资源的动态运行时,并且会经常重启。重启要一次次分析同样的不可变镜像,每次都会浪费掉相同的 CPU 周期。这个分析还会使用大量的内存,而且在 JVM 生命周期中一直占用。但是在 Kubernetes 中,部署密度很重要:可以运行的应用程序实例越多,Kubernetes 的投资回报率就越高。
Kubernetes Native Java 意味着 Java 应用程序可以利用而不是抗拒 Kubernetes 的能力——启动速度很快的小进程,而不是启动时间很长、内存占用很高的 JVM。
2 Quarkus 背后的动机
“在我们推出 Quarkus 之前,许多客户已经开始关注 Go 和 Node.js 等技术栈,希望可以借此提高性能和效率。对于选择一种新的语言以及雇用新的开发人员,或者重新培训现有的 Java 开发人员,这些客户都倍感疲倦。” —— Arijit Mazumdar,WiPro
组织以及开发人员都在 Java 上有很大的投资。它是一种令人难以置信的高产语言,有许多开发人员熟悉并喜爱的神奇工具和框架。然而,Kubernetes 需要一个新的方向。虽然放弃 Java 的许多好处令人遗憾,但能更有效利用内存、缩短启动时间的运行时,如 Node.js 和 Golang,对 Java 投资发起了挑战。

Quarkus 团队则希望两者兼得:将功能丰富、成熟的 Java 生态系统的优势与 Kubernetes 的操作优势相结合。要实现这一点,Java 应用程序运行时必须更多地采用静态定义,但仍然包含让开发人员可以更快迭代的 Java 动态特性。
组织使用云增加了应用程序部署的速度。同样,现如今的业务需要缩短开发周期,加速业务价值交付。Quarkus 所采用的方法特别注重“开发乐趣(Developer Joy)”,可以支持迭代和部署速度的提升,并且已经证明 Java 可以比脚本语言更具生产力。
当然,生产力并不仅仅与编码速度有关。构建现代云应用程序需要与其他服务交互,而使用复杂 YAML 的配套技术会降低你的速度。我们认为,关乎“开发乐趣”的特性,比如开发服务和零配置开发(稍后会详细介绍),对于 Kubernetes Native Java 来说必不可少。例如,你编写访问数据库的代码,Quarkus 会在你输入的时候神奇地启动并连接后台的所有一切。
同样,向 Kubernetes 部署时也不需要阅读 Kubernetes 管理方面的书籍以及编写数百行的 YAML 文件。我们认为,对于 Quarkus,很重要的一点是你只需要了解 Java。在构建时添加“-Dquarkus.kubernetes.deploy”标志,Quarkus 就会将应用程序部署到 Kubernetes 上(不要忘记登录!)如果你愿意,可以使用集成的 Quarkus Dev UI,直接从浏览器触发部署。
简而言之,Quarkus 是从头开始构建的,它将 Java 变成了构建本地二进制文件和 Kubernetes 应用程序的理想语言。Quarkus 是 Kubernetes Native Java!
3 “构建时”促进原生编译
“Quarkus 引入了一种新的范式,它颠覆了底层的运行方式——支持原生模式,所有反射都是在编译时完成,而不是运行时完成,这太神奇了!” —— Roberto Cortez,Talkdesk(目前受雇于红帽公司)
按照 Kubernetes 原生架构的要求,将应用程序的生命周期划分为两个不同的阶段,是实现可瞬间启动的小型轻量级进程的关键。传统上,Java 应用程序运行时启动要执行一系列复杂的、长时间运行的、动态的自省步骤,以满足动态部署环境的要求。这些步骤在应用程序每次启动时都要重复进行。

由于容器镜像不会变化,所以不需要在运行时再执行这些步骤。大多数动态启动步骤都可以移到构建时。在实际开始时执行的工作将少很多,速度会明显加快。此外,还可以丢掉应用程序不需要的代码。通过对输出进行裁剪可以得到只包含必须代码的更精简的可执行文件。
当企业在评估和准备 Java 原生可执行程序时,Quarkus 直接带来了 JVM 效率方面的优势,因为构建时优势普遍适用:从这种方法中获益的不仅仅是原生编译,还有传统的 JVM/Hotspot。当代码较少、效率较高时,无论其形式如何,输出都会更精简、更快速。也就是说,本地提前编译将这种优势提升到了一个新的水平:在一个封闭的世界里,所有东西都是提前知道的,编译器可以进行非常精细的优化,一直到字段、变量和指令集级。
不过,有一个问题。为了使预(AOT)编译器有效地完成工作,它必须能够理解 Java 代码最终会做什么。Java 的动态性使其具有了很高的生产力,同时也使编译器无法掌握应用程序的行为,并限制了优化程度。例如,注入点使代码变得简单,易于演进。但对 AOT 编译器来说,它是一个未定义的、待明确的东西。
例如,你可以通过反射将每个类标记为可用,从而轻松得到一个原生可执行文件。但结果可能会让人失望:内存使用量和启动时间只比 JVM 上好一点点。换句话说,你需要一个全面的构建时模型来释放原生编译的所有优势。就像 Quarkus 所做的那样!
现在,Quarkus 在构建时解析了每个依赖关系,并生成了一个完整而封闭的应用程序。前面提到的注入示例的问题,从 AOT 编译器的角度来说,已经完全解决了——它现在知道要使用什么类,其余的代码可以去掉了。
总之,将 GraalVM 的优化能力与 Quarkus 的构建时能力相结合,可以获得最小的内存占用、最短的启动时间。限制这种开销对于释放 Kubernetes Native Java 的全部优势至关重要。
4 一流的本地编译
“现在,我们是在容器上以 JVM 模式使用 Quarkus,但将来,我们已经计划在 Kubernetes 和无服务器环境中以原生模式使用 Quarkus。在这些环境中,原生模式将是理想的选择。” —— Edouard Lamotte,Sedona
由于 Quarkus 及其扩展(Quarkus 优化过的库)包含了构建时优化,所以你可以将任何应用程序构建为一个内存占用较小的本地可执行文件。Quarkus 负责所有的工作!其他解决方案通常需要更多的开发人员参与:也许你可以编译成本地程序,但前提是你的应用程序使用了明确支持的 API 子集,或者你用了 GraalVM 提示(hints)注解代码。即使你遵循 GraalVM 的规则,也经常需要保证一个单独的 JSON 配置文件得到及时更新。此外,Quarkus 使开发者能够轻松编写针对本地可执行文件的测试,确保你构建的东西(本地可执行文件)按预期运行。

我们认为,平台对原生编译的广泛支持对于有效使用该技术至关重要。出于这个原因,我们不遗余力地简化原生编译的采用。此外,我们确保 Quarkus 中的每个扩展都支持它。我们强烈建议所有“Quarkiverse”扩展(社区贡献的扩展库)也支持原生编译。并非所有的 Java 库都与 GraalVM 的原生编译兼容。然而,由于 Quarkus 有大量的扩展(将近 500 个,而且还在不断增加!),所以很可能你能想到的所有用例都已经覆盖到了。如果没有,为你的用例编写一个 Quarkus 扩展也并不困难。
5 开发乐趣
“在提升开发体验方面,Quarkus 一直有一些相当惊人的创新。他们的开发模式控制台和 Testcontainer 支持将其提升到了下一个等级。” —— James Ward,Java Champion
如前所述,Kubernetes Native Java 并不仅仅是为了精简可运行文件。它对于提高 Java 的生产力,扩大其相对于其他原生编译语言(如 Golang)的生产力优势,同样至关重要。每一种工具、框架和运行时都声称它能提高开发者的生产力。但 Quarkus 通过提供“开发乐趣”将其提升到了一个新的水平。这是什么意思呢?

Quarkus 致力于提供一种有趣的、令人愉快的开发体验,保证 Java 开发者持续参与。Quarkus 的目标是让每个开发者都专注于活跃的编码,因为我们知道,停下来,运行一堆工具,等事情完成,会产生很大的反作用。Quarkus 让开发者非常高效,以至于他们会愿意去做各种尝试——以前,由于截止日期的限制,这可能是不被允许的。以下是几个可以带来“开发乐趣”的特性:
  • 实时编码:所有代码的变更都是实时的,即使是修改依赖关系或配置文件,也不用放弃强类型。每次访问应用程序时,Quarkus 会评估代码的变化,重新生成必要的字节码,并在后台重新加载应用程序以返回更新后的结果,通常只需半秒或更短的时间。实时编码适用于几乎所有的代码修改和重构,无论多么复杂,都不需要 IDE 插件或特殊工具。
  • 开发服务:Quarkus 使用 Testcontainers 在开发和测试期间自动实例化和配置服务,如数据库、缓存和 Kafka。只需添加一个扩展,然后开始编码即可,容器会在后台自动下载和启动!
  • 零配置开发:约定优于配置原则,意味着开发服务不需要配置。Kubernetes 部署 YAML 是针对特定目标自动生成的,无论是 Minishift、OpenShift,还是普通的 Kubernetes。
  • 持续测试:每次保存文件时自动运行测试。运行所有的测试,失败的测试,或者只运行与变化代码相关的测试(Quarkus 会计算出来)。下图展示了一个实时编码的结果及其持续测试的输出,这使 Java 开发像脚本语言一样高效!
  • Dev UI:在开发过程中,在 Web 浏览器中可视化并利用 Quarkus 扩展功能。下图展示了一个带有 Dev UI 组件的示例应用程序,它允许开发者实时更新配置,查看 CDI Bean,部署到 OpenShift,打开 Swagger UI 等等。
  • 命令模式:在启动 Quarkus 开发模式的终端中,通过键盘提供类似 Dev UI 的功能来补充 Dev UI,比如在不重新启动 JVM 的情况下通过一次按键来改变日志级别。
  • Quarkus CLI:一个用于管理 Quarkus 项目的命令行工具。CLI 可以生成和构建项目,管理依赖关系,运行测试,等等。
  • 标准和优秀框架支持:Quarkus 支持像 MicroProfile 这样的标准,JAX-RS、JPA、JTA 等 Jakarta EE 规范,以及 Apache Camel、Hibernate、Kafka、Spring Compatibility API 等几十个框架和工具。
  • Kotlin 支持:除了 Java 外,Quarkus 还支持另外一种流行的 JVM 语言:Kotlin。
  • IDE 集成:Quarkus 插件可用于 IntelliJ、Visual Studio Code 和 Eclipse 等 IDE,具有增强型代码补全等功能。不过,Quarkus 不需要一个 IDE。开发乐趣特性适用于任何编辑器,甚至是 vi 和记事本!
当这些功能结合在一起时,就可以加快开发团队的速度,使他们能够专注于他们最擅长的工作。这就加速了项目的交付,更快地满足客户需求,最终通过把更多的时间花在业务逻辑上而不是管道上来提高应用程序的质量。
6 命令式 vs. 反应式
“[Quarkus] 天然可以处理反应式或命令式编程。” —— LogicDrop

运行时性能很重要。由于整体资源使用率较低,所以反应式编程模型的扩展效率更高,响应性也更强。但是,实现运行时性能最大化不应该要求重写,也不应该以损失易用性和开发灵活性为代价。因此,Quarkus 建立在以 Eclipse Vert.x 工具包为基础的统一的阻塞 / 反应式 I/O 栈上。
Quarkus 实现了智能路由,使命令式和反应式 API 的结合成为一种常规的开发体验。
当一个应用程序使用 Quarkus 的反应式 API 时,代码在 I/O 线程上运行。这就减少了线程上下文切换,并在最大程度上提高了吞吐量,同时最大程度地减少了资源消耗。
当使用命令式 API 开发时,Quarkus 将工作分派给工作线程,完成后再将工作切换回 I/O 线程。
即使在同一个应用程序(或同一个类)的开发中,也可以使用阻塞的命令式 API 或异步的反应式 API。借助 Quarkus 智能路由,Quarkus 天生就为这两种方法并存提供了完美的支持。开发人员不需要在编写代码之前就做出选择。
7 再见了,样本代码!
“[我看到] 开发生产力提升了 30% 到 40%” —— Christos Sotiriou,Vodafone Greece
Quarkus 社区坚信,你在 Quarkus 项目中输入的所有内容都应该是超级简洁且容易理解的,并且不需要过多的使用光标键!Quarkus 包括了许多流行的 API,并对它们做了增强,以便它们可以利用 Quarkus 的构建时信息。

例如,Quarkus 用“Panache”增强了 Hibernate,这是一组 API 扩展,使数据访问像现代 Java 一样自然。这段代码片段就是表示 JPA 实体所需的全部内容。没有 getter,没有 setter,没有样板代码!
@Entitypublic class Person extends PanacheEntity { public String name; public LocalDate birth; public Status status;}
现在,查询数据库也很简单:
List<Person> allPersons = Person.listAll();
Quarkus 扩展可以检测到其他扩展的存在并与之集成,减少需要编写的代码量,趋向于我们认为的最佳模式和实践。
例如,如果“Panache”代码片段属于一个包含数据源和健康检查扩展的应用程序,那么数据源扩展将自动为应用程序定义数据库健康检查和 Kubernetes 就绪探针。因此,当数据库不可用时,Kubernetes 将不会向应用程序容器发送流量。
甚至更进一步,如果同一个应用程序包含一个指标扩展,数据源扩展将自动把指标暴露给 Prometheus 这样的监测工具。
8 将 Kubernetes 作为一等支持平台
“这是否意味着我可以回去写代码了?我不用再处理 [Kubernetes] 服务配置,因为 Quarkus 帮我做了。” —— DevOps 工程师,LogicDrop
Quarkus 天生就是 Kubernetes Native 的,其运行的高效率来自于 Java 字节码和本地可执行文件。此外,Quarkus 将 Kubernetes 作为一等支持平台,并提供了以下扩展和能力:

  • 配置:Quarkus 可以利用 Kubernetes API 来访问存储在 ConfigMap 或 Secret 中的配置信息,而无需将其挂载到 pod 的文件系统中。
  • 应用程序健康:使用 MicroProfile Health 向 Kubernetes 健康探测器(探测潜在的流量重定向和 pod 重启)暴露应用程序的健康状况。
  • Kubernetes 客户端:将 Kubernetes 对象封装在一个 Java 对象模型中,并提供一个 Java API 来操作这些对象。
  • 服务发现和负载均衡:应用程序可以利用 Kubernetes 集群内的 DNS 并借助 Kubernetes 的轮循负载均衡进行服务发现。或者它们可以使用 Quarkus 的 Stork 客户端负载均衡框架来实现更复杂或自定义的负载均衡算法。
  • 精简的 Kubernetes 和 Knative 配置与部署:Quarkus 会生成部署到 Kubernetes 或 Knative 所需的 YAML,后者为 Kubernetes 增加了无服务器功能。可以使用 Quarkus 属性自定义 YAML 的生成。
  • 可观察性:使用 Micrometer 或 MicroProfile Metrics 深入了解正在运行的应用程序,并使用 MicroProfile OpenTracing 或 OpenTelemetry 跟踪多个服务的请求。
  • 函数即服务(FaaS):Quarkus 提供了 Funqy,作为开发可移植函数的 API。这些函数可以在 Knative 上借助 Knative 事件运行,也可以并在 AWS Lambda、Azure Functions 和 Google Cloud Functions 等 FaaS 环境中运行。
  • 远程开发:Quarkus 为远程实时编码提供了开箱即用的支持,而且无需额外的工具。你可以对运行在 Kubernetes Pod(或其他远程环境)中的应用程序进行实时编码——不需要专门的 IDE 工具
  • Quarkus Operator SDK:简化了用 Quarkus 编写 Kubernetes 操作符的工作。
9 Quarkus 入门
“我能够非常迅速地准备好 Quarkus 并运行。Quarkus 指南提供了很大的帮助,因为它们专注于一个主题,而且直接了然。” —— Victor Gallet,高级开发工程师和 Kafka 全能专家
要想了解更多的信息,最好的方法是创建你的第一个 Quarkus 应用程序。只需要几分钟就可以,只有四个步骤!请查阅入门指南,它将引导你完成每个步骤。Spring 开发者可以通过阅读免费电子书 Quarkus for Spring Developers 快速掌握 Quarkus。

之后,你可以使用我们的网站 code.quarkus.io:只要选择你想要的扩展,并生成项目就可以了。
10 小结
Kubernetes Native Java 关乎重新定义使用 Java 包含 Kubernetes 模型的方法,在共享环境中,通过减少启动时间和内存使用率、提高资源效率来降低成本。Quarkus 通过将大部分的启动处理转移到构建时来实现这一目标。其结果是一个高效的运行时,通过资源效率显著提升的应用程序大幅降低云计算成本。此外,企业可以提供直接影响业务的新功能,而又不会超出他们目前的云计算预算。
为了最大限度地发挥原生编译的优势,Quarkus 实现了与 GraalVM 的紧密结合。应用程序经裁剪后,可以充分利用提前编译带来的优化。Quarkus 使用 GraalVM 的扩展模型增强了这一过程,并避免了额外的配置。
Quarkus 提供的“开发乐趣”使开发人员通过实时编码、开发服务和持续测试等功能持续参与解决业务问题。使用 Quarkus 进行开发的效率非常高,Java 开发者可以尝试多种解决问题的方法,并选择对业务最有利的一种。
Quarkus 还将 Kubernetes 视为一等支持平台。它提供了一系列 API 框架,使应用程序能够充分利用 Kubernetes 提供的东西。这实现了 Kubernetes 投资的最大化,并通过平台整合缩短了上市时间。
开始吧!
作者简介:
Jason Greene 是红帽公司的杰出工程师兼应用运行时首席架构师。他是 Quarkus 的联合创始人和共同负责人。该项目旨在革新云端微服务和无服务器应用程序 Java 开发。在此之前,他曾领导 WildFly(业界领先的开源 EE 应用服务器,正式名称为 JBoss AS)项目长达近十年。在红帽任职期间,他曾参与许多中间件领域的工作,包括服务器运行时、集群、Web 服务、AOP 和安全。他是开源的坚定支持者,在职业生涯中为许多项目做过贡献。他也是 JCP 和 Eclipse Jakarta 的成员,他曾代表红帽公司参加 EE 专家组及相关标准的制定。他的兴趣包括并发性、分布式计算、硬件和编程语言设计。
John Clingan 是红帽公司的高级首席产品经理,专注于标准和下一代平台,如 Quarkus。他也是 MicroProfile 的共同创始人和指导委员会目前的负责人。John 有 30 年的行业经验(他年纪有点大了),包括软件开发、系统管理、技术销售和咨询,以及产品管理。他还有 10 多年的授课经验,为加州州立大学和加州大学继续教育学院教授 Java 课程。最近,他还为 O'Reilly 开展了 MicroProfile 和 Quarkus 的现场培训。John 是“Kubernetes Native Microservices with Quarkus and MicroProfile ”一书的作者之一。
Eric Deandrea 是红帽公司的高级首席开发大使,专注于应用开发技术。Eric 有超过 22 年的经验,为金融服务和保险行业设计和制定基于 Java 的解决方案和开发培训计划。他也是各种开源项目的贡献者,包括 Quarkus 和 Spring。最近,Eric 将他的 Quarkus 和 Spring 知识付诸实践,出版了他的第一本书 “Quarkus for Spring Developers”。他喜欢用以 DevOps 为中心的自动化技术来简化相关工作。工作之余,Eric 喜欢在新罕布什尔州的湖上划船。他还喜欢冰上曲棍球和武术,并有空手道黑带。
原文链接:
https://www.infoq.com/articles/native-java-quarkus/
点击底部阅读原文访问 InfoQ 官网,获取更多精彩内容!
今日好文推荐
点个在看少个 bug👇
继续阅读
阅读原文