许多承诺无服务器Java应用程序启动速度超快的解决方案会迫使你在开发人员体验、吞吐量性能或安全性方面做出妥协。我们将向你展示如何实现Java应用程序的超快速启动,而不会有这些问题。
最近出现了几种加快Java启动的解决方案,从编译的应用程序中删除JVM的本机映像(Java应用程序中的启动速度瓶颈)到在启动后拍摄应用程序的快照,以便在恢复时无需完成启动任务。但这些解决方案中的大多数都会对开发人员体验、吞吐量性能或安全性造成一些损害。
大大缩短Java应用启动时间的解决方案应该具有以下特征:
易于开发者在应用中实现。
开发人员可以轻松使用现有的技能和API来编写应用程序。
开发人员可以轻松地在部署(恢复)时启用动态配置。
确保应用的峰值吞吐量性能不会下降。
确保app的安全性不受损害。
我们将简要介绍其中的每一点,并描述我们是如何通过Liberty InstantOn实现这些目标的。
Liberty InstantOn检查点/恢复解决方案
Liberty InstantOn是一个基于检查点/恢复的解决方案,用于在无服务器环境中快速启动Java应用程序。与其他解决方案不同,Liberty InstantOn从一开始就由开发JDK(IBM Semeru Runtimes,OpenJDK和Eclipse OpenJ9的免费发行版)和应用程序运行时(Open Liberty,开源Java应用程序运行库)的开发团队共同设计。这种协作反映了应用程序运行时和JDK之间必须协作完成的许多检查点和恢复任务:对Liberty进行了几项更改,要么将任务延迟到检查点之后(例如,Liberty安全功能的初始化),要么在检查点之前完成任务(例如,等待后台任务完成,例如正在进行的JIT编译和Liberty特定的初始化程序)。
我们测试了三种不同应用程序的性能,分别是不使用(基线)和使用Liberty InstantOn,测量了应用程序的启动时间和服务第一个请求所需的时间。有关结果,请参阅下面的两张图,这三个应用程序的范围从具有单个REST端点(pingperf)的非常简单的应用程序到使用JPA和远程数据库(REST-Crud)的更复杂的应用程序,再到使用MicroProfile功能的完整应用程序(AcmeAir Microservice Main)。
协作式Liberty InstantOn检查点/恢复方法不仅可以提供非常快的启动和首次响应时间,还可以提供比完全移除JVM或仅在JDK级别实现检查点/恢复解决方案,然后声明许多任务不应在检查点之前完成更好的开发人员体验。
易于在应用程序中实现
仅在JDK级别设计的检查点/恢复解决方案迫使你作为应用程序开发人员思考底层操作系统工具CRIU的工作原理以及如何设置它。它们还要求你确定何时对应用程序进行检查点检查,如何确保应用程序在检查点时处于安全状态,如何确保应用程序运行时和JVM也处于安全状态,而不控制任何一个,以及如何确保三者(应用程序、应用程序运行时和JVM)稍后都能正常恢复。虽然在JDK层提供检查点/恢复支持至关重要(这样Java应用程序就不需要直接与CRIU接口),但是与更高级别的应用程序运行时(如Liberty)的良好集成可以大大减轻你的负担,使你可以专注于应用程序的业务逻辑。
有了Liberty InstantOn,你需要做的就是使用官方的Liberty container映像将它容器化,以使你的Java应用程序启动更快。在你的构建配置中,你只需要选择何时执行检查点(在两个“阶段”中的一个,方便地命名为“beforeAppStart”和“afterAppStart”),其余的由Liberty和Semeru的组合透明地处理。然后,你只需在官方的Open Liberty容器映像之上构建一个应用程序层,其中包括Semeru和你需要的所有其他内容。
使用现有技能和API编写应用程序
从应用程序中删除JVM的快速启动解决方案迫使你改变对开发应用程序的想法。此外,大多数企业软件都很复杂,需要大量的组件重用。对其他开源项目的任何依赖都意味着无限期地等待所有这些项目得到适当的更新,以符合可以在没有JVM的情况下工作的标准Java特性的子集。
Liberty InstantOn执行各种任务,允许你坚持使用熟悉的思维模型来理解Java应用程序应该如何工作。动态类加载、反射和动态JIT编译都是你在应用程序中习惯使用的东西,也是你作为Java开发人员思考方式的一部分。Liberty InstantOn可以与Semeru云编译器无缝协作,这实现了JIT编译的好处,而无需为恢复的应用程序的每个实例加载内存和CPU。Liberty InstantOn还支持常用的API新旧规范,包括Jakarta EE、MicroProfile和Spring Boot(目前处于测试版)。因此,你的新应用程序和现有应用程序都可以被容器化,以更快地启动。
在部署(恢复)时启用动态配置
快速启动的本机映像解决方案需要你重新编译应用程序,并重新构建应用程序容器映像,以便调试某些需要进一步诊断信息的问题,或者禁用某些配置或优化。
使用Liberty InstantOn,你的运营团队已经为你收集了跟踪信息,因为他们可以在打开方法跟踪的情况下轻松地重新部署应用程序的实例,而无需重新编译应用程序。Liberty和Semeru允许你在恢复时重新配置部署,并在随后进行修改。这些可维护性的改变支持实际的用例,比如启用方法跟踪或者改变一些Liberty行为来解决部署(恢复)时的问题。重要的是,作为开发人员,你在构建应用程序容器映像时不需要考虑大部分的可服务性问题,因为它们都由Liberty InstantOn处理。
该应用的峰值吞吐量性能不会下降
虽然零扩展和容器部署使启动时间成为一个重要的性能指标,但应用程序吞吐量仍然是一个重要的考虑因素,因为它最终会影响服务的长期运营成本。如果你通过从应用程序中移除JVM来提高启动速度,你将失去动态JIT编译和Java的垃圾收集(GC)技术的好处。使用推测优化的动态JIT编译是Java平台的一个关键价值主张。
用Liberty InstantOn保留JVM意味着你的应用程序获得了动态JIT编译和GC技术的所有好处,这些技术已经在Semeru JDK磨砺了几十年。恢复后的容器只是从执行检查点之前停止的地方继续执行,因此通常会达到与不使用InstantOn的传统Liberty容器相同(或非常接近)的峰值吞吐量(正如你在下面的图表中从前面描述的相同三个应用程序的测试中看到的)。
应用程序的安全性没有受到影响
快速启动的检查点/恢复解决方案可能会损害企业部署中最重要的考虑因素之一:安全性。
首先,Liberty InstantOn不要求应用程序以root身份运行或使用特权容器。Liberty和Semeru开发团队与CRIU项目合作,将恢复所需的Linux功能集减少到一个足够小的集,从而在生产中不存在安全问题(请参阅我们如何开发Eclipse OpenJ9 CRIU支持快速Java启动)。团队打包了Liberty和Semeru容器映像,这样你就不需要自己管理Linux功能。
第二,如果容器映像中包含敏感信息(如密钥),并且该映像随后被发布到容器储存库,则该敏感信息可能会被泄露;此类敏感信息必须仅保留在部署环境中。JDK不允许在检查点之前使用大多数加密算法。基于Liberty构建的应用程序只在恢复时打开网络连接,这通常是需要密钥和其他秘密的地方。对于检查点端仍然允许的有限的一组加密操作,Semeru确保清除所有敏感信息,以便检查点中没有安全风险。
不折不扣的快速启动和流畅的开发者体验
虽然有一系列方法可以缩短无服务器计算的Java启动时间,但是开发人员的体验必须尽可能的流畅,这样才不会给应用程序开发人员带来不必要的额外责任、学习和工作。我们重申,解决方案必须::
l 易于开发者在应用中实现。
l 开发人员可以轻松使用现有的技能和API来编写应用程序。
l 开发人员可以轻松地在部署(恢复)时启用动态配置。
l 确保应用的峰值吞吐量性能不会下降。
l 确保app的安全性不受损害。
我们努力在基于检查点/恢复的解决方案中解决所有这些需求,不仅针对新应用程序,也针对现有应用程序。Liberty InstantOn旨在提供出色的开发人员体验,同时不影响产品性能。Liberty InstantOn仍有一些局限性,在团队继续努力解决这些局限性的同时,这些局限性会得到明确和公开的记录。