java流行的开发框架:作为Java开发者
java流行的开发框架:作为Java开发者这一直是 Quarkus 设计的重要组成部分。当把应用程序编译为本地映像时,它的启动速度会更快,并且可以用比标准 JVM 小得多的堆栈参数来运行。Quarkus 已经通过了 Substrate 的所有测试,可以在没有 -H: ReportUnsupportedElementsAtRuntime 标志的情况下运行。从一开始, Quarkus 就围绕容器优先理念进行设计。通过以下方式针对低内存使用量和快速启动时间进行了优化:Quarkus 与 Spring 等主流框架的异同Quarkus 是基于 J2EE( Jakarta EE )和 MicroProfile 标准来作为技术栈,而 Spring 有自己的一套东西,大家可以从 https://simply-how.com/ Quarkus -vs-spring 来看两者的区别。其实 Spring 社区也开始在实践用 Graal VM 来构建
随着各种开放云平台的出现,传统的基于 java 和 J2EE 的编程模型和框架在云环境下无法适应,高内存需求和启动速度缓慢等限制了它们在云平台的扩展能力,面向云原生的编程框架需求变得越来越多。2019 年红帽发布了基于云原生的 Java 框架 Quarkus,本文作者冯征从 Quarkus 项目背景、设计特色、应用场景、开发难点等方面做了全方位剖析,希望给关注 Quarkus 框架的开发者带来一些帮助和思考。他还将在 QCon 全球开发大会(北京站)2020 分享 Quarkus 的更多技术动态,敬请关注!
Quarkus 项目背景
2018 年,在一次内部会议中,有人问到“有什么会成为红帽中间件最大的威胁?”,红帽中间件副总裁 Mark Little 的回答让我印象很深。他最担心的事情是,如果一种新的编程语言(比如 Go)能够给用户提供更快地运行速度,并且提供更丰富的类库来支持从 Java 应用的迁移,那么在云环境不断完善并进入用户的生产系统后,这种应用替换和迁移的代价在不断降低,使得用户会更倾向于使用新的编程语言来进行开发。而红帽所有基于 Java 的中间件产品都用新的编程语言来重构一遍几乎是不可能的。所以我们必须要让 Java 更快一些。
后来,红帽内部建立了一个“秘密”项目 Protean ,也就是 Quarkus 早期的名称。当时只有少数的开发人员参与设计并开发原型系统。早期测试的时候,我们在 Kubernetes 中单纯利用 Wildfly 或者 JBoss EAP 的 docker 镜像来启动用户的应用,最多只能扩展到 100 多个实例,而且启动的时间很长。而利用 Quarkus 产生的 Native 应用 docker 镜像,可以轻松地扩展到 1000 多个示例,是前者的 10 倍多,并且启动时间大大缩短。这些都促使红帽中间件在 Quarkus 的创新,使之成为红帽整个 Open Hybrid Cloud Strategy(开放混合云架构)中重要和不可或缺的一部分。
Quarkus 与 Spring 等主流框架的异同
Quarkus 是基于 J2EE( Jakarta EE )和 MicroProfile 标准来作为技术栈,而 Spring 有自己的一套东西,大家可以从 https://simply-how.com/ Quarkus -vs-spring 来看两者的区别。其实 Spring 社区也开始在实践用 Graal VM 来构建 Native 应用,项目网站是 https://github.com/spring-projects-experimental/spring-graal-native。我们可以看到,目前这个项目还是处于验证阶段,和 Quarkus 相比还有很远的距离。
Quarkus 设计特色
从一开始, Quarkus 就围绕容器优先理念进行设计。通过以下方式针对低内存使用量和快速启动时间进行了优化:
- 对 Graal / SubstrateVM 的支持
这一直是 Quarkus 设计的重要组成部分。当把应用程序编译为本地映像时,它的启动速度会更快,并且可以用比标准 JVM 小得多的堆栈参数来运行。Quarkus 已经通过了 Substrate 的所有测试,可以在没有 -H: ReportUnsupportedElementsAtRuntime 标志的情况下运行。
- 构建阶段的元数据处理
在构建阶段将进行尽可能多的处理,因此应用程序将仅包含运行时实际需要的类。在传统模型中,所有的类都会在应用程序初始化时进行处理,即使它们仅使用一次。而使用 Quarkus ,它们甚至都不会加载到运行时的 JVM 虚拟机中。因为在构建阶段我们就尽量完成初始化的工作,这将减少应用在运行时的内存使用量,并缩短应用的启动时间。
- 减少反射使用
Quarkus 尽可能避免使用 Java 的反射功能。
- Native 原生应用的提前引导
当使用 Native 原生应用运行时, Quarkus 在 Native 映像的构建过程中提前引导尽可能多的框架代码。这意味着生成的 Native 映像已经执行了大多数启动代码,并将结果序列化到最终的可执行映像文件中,从而使应用的启动速度更快。
Quarkus 的内核是围绕着 CDI 设计,整个核心就是一个微型的 CDI 容器,而且完全支持异步的编程模型比如 Netty 和 Vert.x ,可以支持直接利用 Kotlin 语言进行开发。整个 Quarkus 框架采用了 Extensions 进行扩展,其中包含了目前红帽中间件大部分的产品,比如 Hibernate ORM、 Artemis、Resteasy、Undertow、Narayana、 Infinispan、Camel、KeyCloak 等等。所以对用户来说,是可以轻松的利用这些框架和工具来进行开发。
从内部的 Quarkus 0.1 测试版本开始,到目前刚刚发布 1.4.1.Final , Quarkus 的开发速度是很快的,不断带来更多的 Extension 扩展来丰富功能。值得注意的是,从 Quarkus 1.4 版本开始,JDK 8 将被标记为过时,而从 1.6 版本开始, Quarkus 将不再支持 JDK 8,而会支持 JDK 11 的 LTS 版本。
Quarkus 应用场景
Quarkus 主要应用场景是开发云原生应用,用户可以轻松利用 Quarkus 生成 Native 映像并进行部署。当然, Quarkus 不仅仅可以运行在 Kerbenetes 环境中,也可以运行在 JVM 虚拟机环境中,甚至可以运行在用户本地的 IDE 开发环境中。它解决的核心问题是加速 Java 程序的启动和运行速度以及更小的运行时内存占用。用户非常惊叹 Native 应用的启动速度,往往能比正常的 Java 应用快 10 倍以上。而且经过优化以后, Native 的映像文件可以做的很小,非常适合在云环境中使用。
我们来看一下内存使用和启动速度的比较:
Quarkus 开发难点
其实 Quarkus 有很多的脚手架工具来帮助用户搭建开发环境,用户也可以从网站 code. quarkus .redhat.com 来轻松的生成项目工程。开发者利用 Maven 或者 Gradle 来构建应用也是非常的方便。如果要说最难的地方的话,可能会是开发 Quarkus 的 Extension 扩展。我目前的工作就是把 Camel 的组件加入到 Quarkus 中成为扩展,这样用户就可以方便的在 Quarkus 应用中使用这些 Camel 组件。
我会通过一个具体的示例来说明如何进行 Quarkus 的扩展开发。目前 Quarkus 还不支持 Scratch 方式来构建扩展项目,所以只能是在 Quarkus 的代码树中来新增扩展。比如我们想要加一个新的扩展 Upper-extension ,它可以提供应用调用的方法 Upper.convert(String str)把字符串 str 全部转换成大写:
public class Upper { public String convert(String str) { return StringUtils.upperCase(str); }}
首先我们需要从 Github 下载或者复制 Quarkus 源代码:
git clone https://github.com/quarkusio/quarkus.gitcd quarkuscd extensionsmvn io.quarkus:quarkus-maven-plugin:1.4.2.Final:create-extension -N \ -Dquarkus.artifactIdBase=upper \ -Dquarkus.artifactIdPrefix=quarkus- \ -Dquarkus.nameBase="Upper Extension"
生成的 Maven 工程包含两个子项目 Deployment 和 Runtime 。我们来分别看看这两个子项目,首先在 Deployment 中主要包含 Quarkus 在构建阶段所需要做的工作,带有 @BuildStep 注解标记的方法都会在构建阶段执行,比如在 UpperProcessor.java 文件中
@BuildStepFeatureBuildItem feature() { return new FeatureBuildItem(FEATURE);}
这表明会返回一个包含当前扩展名称的 BuildItem。
各个构建步骤之间可以通过 BuildItem 来传递信息,比如上面的 FeatureBuildItem 当有其它的构建步骤需要获得这个信息的时候可以作为参数来使用:
@BuildStepvoid printFeatures(List<FeaturesBuildItem> features) { foreach(FeatureBuildItem feature : features) { System.out.println(feature.getInfo()); }}
我们需要在 deployment/pom.xml 中使用如下代码处理这些 @BuildStep 注解标记的方法:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <annotationProcessorPaths> <path> <groupId>io.quarkus</groupId> <artifactId>quarkus-extension-processor</artifactId> <version>${quarkus.version}</version> </path> </annotationProcessorPaths> </configuration></plugin>
那么如何在构建阶段创建类并实现静态初始化操作呢 ?在这里我们就需要用到 Bytecode Recording(字节码记录)。
在 Runtime 子项目中新建一个 UpperRecorder.java :
@Recorderpublic class UpperRecorder { public void createUpper(BeanContainer container) { Upper upper = new Upper(); container.instance(UpperProducer.class).setUpper(upper); }}
注意这里的 @Recorder 注解标记, Quarkus 会把构建阶段每次的方法调用都序列化,并通过生成 Bytecode 的方式保存下来。所以 CreateUpper 方法中的操作在构建阶段都不会立即执行,而是会转换成 bytecode 并延迟到运行时再执行。
而在构建阶段,我们可以做的初始化工作,包括设置参数,扫描特定的 Annotation 并注册(我们在 Camel- Quarkus 中也使用了)等等。这样做的好处是在构建阶段可以尽量多的完成初始化工作,这样运行时启动应用的速度会大大加快。如果是要通过 Graal VM 生成 Native 映像的话,有些初始化工作是不能在构建阶段做的,比如监听网络端口,启动线程池等等。
回到 Deplolyment 的 UpperProcessor 中,我们增加一个构建步骤来创建 Upper :
@BuildStep@Record(ExecutionTime.STATIC_INIT)void create(UpperRecorder recorder BeanContainerBuildItem beanContainer) { recorder.createUpper(beanContainer.getValue());}
这样我们基本完成了 Upper 扩展,为了让用户可以在 CDI 环境中使用 Upper,我们还需要在 Runtime 中新建 UpperProducer.java :
@Singletonpublic class UpperProducer { private volatile Upper upper; void setUpper(Upper upper) { this.upper = upper; } @Produces Upper getUpper() { return upper; }}
然后在 deployment/UpperProcessor.java 中加入:
@BuildStepAdditionalBeanBuildItem upper() { return AdditionalBeanBuildItem.unremovableOf(UpperProducer.class); }}
使得 Quarkus 把 UpperProducer 也当成 CDI Bean 来处理,可以在依赖注入中使用 Upper 。
最后用户就可以在应用中这样来使用:
@ApplicationScopedpublic class MyService { @Inject Upper upper;
public String onMessage(String message) { return upper.convert(message); }}
总的来说,在 Extension 的扩展中,我们使用 @BuildStep 来标记各种构建步骤,并且尽量在构建阶段完成初始化工作,这样可以减少通过 Graal VM 生成的可执行映像文件大小,加快应用的启动速度,缩短启动时间。
Quarkus 进展与展望
很多公司在早期的 Tech Pre 版本开始就在关注 Quarkus ,比如(Ericsson、Amadeus)。随着 Quarkus 正式 GA 产品的发布,我相信会有很多的公司加入到 Quarkus 中。红帽一直以来的都坚持采用“社区驱动”的产品开发模式,未来,红帽中间件的所有产品都计划考虑加入到 Quarkus 中,这样可以让用户之前基于 J2EE 开发的应用能够在 Quarkus 中进行测试。
云原生应用越来越重要,大量基于 Java 开发的应用都面临着同样的问题,比如在云环境中如何快速的部署和提高应用启动速度。Quarkus 是红帽开放混合云战略的重要组成部分,也是红帽中间件下一代的核心产品。作为云原生应用的开发利器,Quarkus 将帮助用户利用 Jakarta EE 和 MicroProfile 技术栈更快和更便利的开发产品,并快速部署到云环境中。作为 Java 开发者,需要对云原生应用有更多的了解,并对 Quarkus 这样的新框架熟悉掌握,最后也希望能够有更多的开发者关注 Quarkus 并积极参与到社区里。我们期待推出的产品是经过社区用户的大量反馈,并且也能真正适应开发者需求的。