红帽为何要推出基于云原生的编程框架 Quarkus

发布于:2020 年 6 月 1 日 16:06

红帽为何要推出基于云原生的编程框架 Quarkus

导读:随着各种开放云平台的出现,传统的基于 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 设计特色

从一开始, 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 开发难点

其实 Quarkus 有很多的脚手架工具来帮助用户搭建开发环境,用户也可以从网站 code. q uarkus .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.git
cd quarkus
cd extensions
mvn 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 文件中

复制代码
@BuildStep
FeatureBuildItem feature() {
    return new FeatureBuildItem(FEATURE);
}

这表明会返回一个包含当前扩展名称的 BuildItem。

各个构建步骤之间可以通过 BuildItem 来传递信息,比如上面的 FeatureBuildItem 当有其它的构建步骤需要获得这个信息的时候可以作为参数来使用:

复制代码
@BuildStep
void 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 :

复制代码
@Recorder
public 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 :

复制代码
@Singleton
public class UpperProducer {
    private volatile Upper upper;
    void setUpper(Upper upper) {
        this.upper = upper;
    }
    @Produces
    Upper getUpper() {
        return upper;
    }
}

然后在 deployment/UpperProcessor.java 中加入:

复制代码
@BuildStep
AdditionalBeanBuildItem upper() {
    return AdditionalBeanBuildItem.unremovableOf(UpperProducer.class);
    }
}

使得 Quarkus 把 UpperProducer 也当成 CDI Bean 来处理,可以在依赖注入中使用 Upper 。

最后用户就可以在应用中这样来使用:

复制代码
@ApplicationScoped
public 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 并积极参与到社区里。我们期待推出的产品是经过社区用户的大量反馈,并且也能真正适应开发者需求的。

作者介绍:

冯征,红帽 JBoss 中间件团队高级软件工程师。2009 年加入红帽,有 10 余年的开发经验,作为核心成员参与 JBossTM 事务管理器(Narayana)的开发。目前在 JBoss Fuse 团队做 Camel 项目的研发工作,在上游的开源项目(Narayana、WildFly、Camel、CXF、Camel-K、Camel- Quarkus )中也做过贡献。


QCon 北京 2020 的演讲中,冯征老师将介绍 Quarkus 框架设计和特点,利用 Quarkus 进行应用开发、利用 Native 模式在云平台进行部署的实践案例。点击了解更多。

阅读数:1125 发布于:2020 年 6 月 1 日 16:06

评论

发布
暂无评论