blob: 710259a6281d49bab2d5b167d1e1374f5ba35f02 [file] [log] [blame]
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Apache Dubbo – Native Image</title><link>https://dubbo.apache.org/zh-cn/tags/native-image/</link><description>Recent content in Native Image on Apache Dubbo</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sat, 07 Oct 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://dubbo.apache.org/zh-cn/tags/native-image/index.xml" rel="self" type="application/rss+xml"/><item><title>Blog: 启动速度提升10倍:Apache Dubbo 静态化 GraalVM Native Image 深度解析</title><link>https://dubbo.apache.org/zh-cn/blog/2023/10/07/%E5%90%AF%E5%8A%A8%E9%80%9F%E5%BA%A6%E6%8F%90%E5%8D%8710%E5%80%8Dapache-dubbo-%E9%9D%99%E6%80%81%E5%8C%96-graalvm-native-image-%E6%B7%B1%E5%BA%A6%E8%A7%A3%E6%9E%90/</link><pubDate>Sat, 07 Oct 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/10/07/%E5%90%AF%E5%8A%A8%E9%80%9F%E5%BA%A6%E6%8F%90%E5%8D%8710%E5%80%8Dapache-dubbo-%E9%9D%99%E6%80%81%E5%8C%96-graalvm-native-image-%E6%B7%B1%E5%BA%A6%E8%A7%A3%E6%9E%90/</guid><description>
&lt;p>摘要:本文整理自杭州有赞科技有限公司中间件技术专家、Apache Dubbo PMC华钟明在 Community Over Code 2023 大会上的分享。本篇内容主要分为五个部分:&lt;/p>
&lt;ul>
&lt;li>一、GraalVM 直面Java应用在云时代的挑战&lt;/li>
&lt;li>二、Dubbo 享受 AOT 带来的技术红利&lt;/li>
&lt;li>三、Dubbo Native Image 的实践和示例&lt;/li>
&lt;li>四、Dubbo 集成 Native Image 的原理和思考&lt;/li>
&lt;li>五、Dubbo 在 Native Image 技术的未来规划&lt;/li>
&lt;/ul>
&lt;h2 id="一graalvm-直面java应用在云时代的挑战">一、GraalVM 直面Java应用在云时代的挑战&lt;/h2>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>云计算时代比较显著的特点包括:&lt;/p>
&lt;ul>
&lt;li>基于云计算的基础设施,Java应用能够在云计算的基础设施上快速、轻松、高效的做到弹性。&lt;/li>
&lt;li>基于容器化技术,系统资源切分的更加细,资源的利用率也更高了。&lt;/li>
&lt;li>基于云计算的开发平台,让应用部署的更加容易,更加敏捷。&lt;/li>
&lt;/ul>
&lt;p>那么在云计算时代,Java应用存在哪些问题呢?&lt;/p>
&lt;ul>
&lt;li>冷启动速度较慢。&lt;/li>
&lt;li>应用预热时间过长,无法立即达到性能峰值。&lt;/li>
&lt;li>内存、CPU等系统资源占用高。&lt;/li>
&lt;li>Java构建的应用程序繁重,执行还需要具备JDK环境。&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_1.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>在Serverless场景上,Java的问题会尤为突出,因为Serverless不仅能简化开发场景和开发体验,还能做到极致的弹性,甚至是秒级的弹性。&lt;/p>
&lt;p>上图是Datalog统计的Fast和AWS两个产品。Java语言虽然更流行,但相较于Python和Node.JS,它的占比还是比较低的。Java本身在Serverless层面,比如在做容器的调度、镜像的下载的时候,启动时间、冷启动的时间、预热时间等等,都会影响Serverless场景下它弹性扩容的时间。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_2.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>下面介绍一下GraalVM,它是可以把Java应用提前编译到独立的二进制包内,这些二进制包相对于跑在JVM上它可以更小,更快的启动,不需要预热就能够达到极限的峰值,还可以减少内存和CPU的占比。&lt;/p>
&lt;p>可以看到它的介绍和Java语言的应用所涉及到的问题都一一对应。GraalVM应该算是JDK的&amp;quot;超集&amp;quot;,除了包含完整的JDK发行版本外,还有GraalVM Compiler、Native image、Truffle等,甚至还涉及到多语言汇编的能力。&lt;/p>
&lt;p>总结一下,GraalVM本身涉及两部分,JIT和AOT。&lt;/p>
&lt;ul>
&lt;li>JIT,是在编译后的class文件、字节码文件,它会在运行时把它翻译成机器码。&lt;/li>
&lt;li>AOT, 它和JIT的区别是,它在编译期就能把字节码直接转化为机器码,无需在运行时再去处理。所以它的CPU和内存会相对更低。&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_3.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>上图左侧是一张Java生命周期的全景图。可以看到,它从JVM的启动,再到Java的main函数的启动,再到Java的应用预热,再到它的稳定期,最后到达效果,这是Java完整的生命周期的呈现。&lt;/p>
&lt;p>而AOT的区别在于,它没有红色的VM。另外,JIT相对于AOT而言是没有的,也没有浅绿色的解释器。所以AOT对于JIT来说,只有内加载,GC以及它能够瞬间达到应用的稳定期。&lt;/p>
&lt;p>根据右侧的图可以看出:&lt;/p>
&lt;ul>
&lt;li>AOT的启动耗时相对较低,内存损耗和它打出来的二进制包相对较小。&lt;/li>
&lt;li>JIT因为有及时编译的效果,所以现在极限的分值比AOT要好,比如它的极限吞吐量比AOT好。&lt;/li>
&lt;/ul>
&lt;h2 id="二dubbo-享受-aot-带来的技术红利">二、Dubbo 享受 AOT 带来的技术红利&lt;/h2>
&lt;ol>
&lt;li>多产物形态&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_4.png" alt="dubbo-graalvm-native-image.png">&lt;/p>
&lt;p>我们在编码之后,Soft Code的产物形态新增了。&lt;/p>
&lt;p>第一种是我们传统认知上的Jar包形态,比如mvn、clean、package。第二种是Docker Image,它能轻松的帮我们直接打到镜像里面去,不用写dockerfile等文件。第三种是我们集成GraalVM后新产生的一种Native可执行文件的形态。这种形态无需JDK的环境就能启动,它能像GO一样把二进制文件直接启动。&lt;/p>
&lt;ol start="2">
&lt;li>启动耗时大幅降低&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_5.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>上图的跑分都是基于4c16g的micro24的系统上跑出来的。左边和右边的区别是,左边的Provider端提供了一个Dubbo服务跑出来的,右边是提供了一个调用者的身份跑分出来的。从左边这张图可以看到,Native的可视性文件比Jar包方式的启动耗时降低了12倍+,在客户端应用,它的启动耗时降低了11倍+。所以在刚刚提到的Serverless场景上,它能提供一个非常好的启动速度。在扩容的时候能够达到秒级,甚至达到毫秒级。&lt;/p>
&lt;ol start="3">
&lt;li>启动后立即达到性能峰值&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_6.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>上图也是跑分跑出来的数据,可以看到Consumer和Provider端通过静态化执行文件执行后,比都为Jar包的情况,第一次调用的耗时降低6倍。这第一次调用代表的是预热的时长,以及第一次需要解析的类,包括资源的情况等等。这让我们在Serverless场景下能够瞬间达到性能峰值。&lt;/p>
&lt;ol start="4">
&lt;li>内存损耗大幅降低&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_7.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>上面有左右两张图,在Dubbo应用的基础上,它的内存损耗也降低大概3.5倍。Native静态化执行文件可以做到60M的内存占比,在客户端它的内存损耗也大概降低了4倍。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_8.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>这是Dubbo在Native Image技术场景中做的努力:&lt;/p>
&lt;ul>
&lt;li>易用性增强:在注解和XML方式中自动识别服务接口,生成Reachability Metadata。&lt;/li>
&lt;li>可维护性增强:自动生成Source code,减轻了Dubbo开发者维护Adaptive的维护成本。自动扫描生成Dubbo core 所需的Reachability Metadata。&lt;/li>
&lt;li>多平台的支持:Linux、MacOS、Windows。&lt;/li>
&lt;li>多功能的覆盖:Dubbo、Triple协议。&lt;/li>
&lt;/ul>
&lt;h2 id="三dubbo-native-image-的实践和示例">三、Dubbo Native Image 的实践和示例&lt;/h2>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_9.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>首先需要安装Dubbo Native Image,这里就不过多介绍了,大家可以根据官方的文档进行下载。&lt;/p>
&lt;p>然后安装插件,可以看到上图中有三个插件需要安装,但和Dubbo相关的只有一个,是Dubbo Maven Plugin。除此之外,还有Spring Boot 的Maven Plugin,它提供的是Spring的AOT的能力。如果是API的接入方式我们无需加这个插件,如果是XML和注解就还需要加这个插件。第三个是GraalVM提供的插件,可以看到这里加了一个reachabilitu-metadata的执行,这个后面会介绍到。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_10.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>然后配置依赖,现在Dubbo关于可达性元数据的扫描、执行,以及配置文件和Source code的生成都在Dubbo Native的依赖下面,我们需要引入这个依赖。&lt;/p>
&lt;p>另外,注解和XML的还是需要接入一个Spring6的兼容的包。因为Dubbo现在还兼容JDK 8的版本,而Spring6发布后支持的最低版本是JDK 17,所以我们还是需要有这么一个模块做一下兼容。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_11.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>最后还需要加一个插件和依赖,然后就能轻松的把应用转化成Native Image的形式。&lt;/p>
&lt;p>最下面是完整的代码示例,大家感兴趣的话,可以尝试一下编译打包,看一下执行的效果。&lt;/p>
&lt;h2 id="四dubbo-集成-native-image-的原理和思考">四、Dubbo 集成 Native Image 的原理和思考&lt;/h2>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_12.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>首先看一下Dubbo集成GraalVM Native Image技术的发展历程。2021年6月我们发布了Dubbo 3.0版本,初步探索了Native Image技术,提供了一个实验性的版本和Demo,涉及到的问题包括以下三个。&lt;/p>
&lt;ul>
&lt;li>维护繁重的Adaptive源码、维护Dubbo所需的全量Reachability Metadata。可能涉及到的问题是,比如要新增一个功能,我们需要考虑coding以及code review的时候是否要添加可达性的元数据,这也会造成维护成本非常高。&lt;/li>
&lt;li>仅支持API方式,不支持注解和XML方式。&lt;/li>
&lt;li>dubbo-native-plugin的插件,在后面3.1版本的时候,Native Image的发展并不多。&lt;/li>
&lt;/ul>
&lt;p>2022年11月Spring6+Spring Boot3正式发布了,它把Native Image技术作为发布的亮点展现给大家。但Dubbo社区认为我们应该对Native Image技术有一个新的跨越,所以在2023年4月发布了Dubbo 3.2版本,将Native Image技术进行了思考和重构。支持以下四个方面:&lt;/p>
&lt;ul>
&lt;li>编译阶段自动生成Adaptive源码,已经不需要开发者维护了。&lt;/li>
&lt;li>支持编译阶段自动生成Dubbo所需的Reachability Metadata。减小了打在业务启动的可执行文件里包的大小。因为在之前的版本里,它是把Dubbo所涉及的全量的可达性的元数据都打在二进制包里。比如业务用jka作为注册中心,我们把nacos也打在那里面,这就会导致它的二进制包非常大。现在这个版本如果用到jka,我们在box上解析不到nacos相关的依赖。所以我们就不会再把相关的内容打进去了,而且现在都是自动识别的。&lt;/li>
&lt;li>新增dubbo-maven-plugin,用于替代dubbo-native-plugin。因为dubbo-native-plugin的能力和内容是比较聚焦的,我们把它重新归名为dubbo-maven-plugin后,涉及到的是我们能够降低业务接入的薪资负担。后面Dubbo相关的maven插件,都通过它来做就行了。&lt;/li>
&lt;li>兼容了Spring6+Spring Boot3,这就是我们在下个版本把XML和注解支持上去的技术的基础。&lt;/li>
&lt;/ul>
&lt;p>2023年12月将会发布Dubbo 3.3x版本。它基于Spring6,完成对XML和注解方式的支持,还移除了dubbo-native-plugin。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_13.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>刚刚提到了非常多次Reachability Metadata可达性的元数据,到底是什么呢?&lt;/p>
&lt;p>我们先来聊一聊AOT的局限性,它本身就有局限,它遵循封闭世界的一个假设。比如它只关注能看到的所有字节码的信息,这就会带来一个问题,Java动态语言的功能就不再支持了。也及时说JNI、Java反射、动态代理、ClassPath资源的获取等都不再支持。&lt;/p>
&lt;p>我们知道,在组件/业务开发/使用产品上涉及到的这些功能是非常多的,既然GraalVM出了这个功能,当然也考虑到了这个问题,所以它利用Reachability Metadata的能力解决了这些问题。&lt;/p>
&lt;p>右边的这张图分为了五类,它涉及到了JNI、反射、序列化、resource、proxy相关的元数据的配置信息。它可以让开发者在编译前就提供好这些元数据信息,提前打包到可执行的二进制文件中。还有第六种分类,是预定义的类型,他需要提供完整的字节码Hash值,所以就没列出来,它可能需要和Tracing的agent联合起来使用。&lt;/p>
&lt;p>下面我们主要介绍一下这五类。&lt;/p>
&lt;p>第一,GraalVM 提供了Tracing Agent来辅助应用采集Reachability Metadata,也就是说在运行期间它会去采集你的行为,比如你要用到反射,他会把反射的元素自动生成出来,然后生成出一个配置文件提供给你。但是并无法确保把所有都采集完整。&lt;/p>
&lt;p>第二,GraalVM提供了Reachability Metadata Repository,用于管理三方组件的Reachability Metadata。我们在Java反射、动态代理这种纯业务的场景,用的相对较少,在运用到组件中相对多一点。举个例子,比如Native里用到反射,我们可以在仓库中直接找到Native反射的metadata。然后通过刚刚提到三个插件中的Native自身提供的插件,它会把仓库里的元数据直接打在二进制包里,我们也就不需要关心这些公共组件的元数据信息了。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_14.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>和Dubbo相关的元数据信息包括以下五类:&lt;/p>
&lt;p>第一,反射的元数据。&lt;/p>
&lt;ul>
&lt;li>内外部的service。我们知道Dubbo是rpc框架,所以定义服务接口是最通用的一种能力。内部提供的服务包括metric service、metadata service,用户服务就是业务定义的服务接口。&lt;/li>
&lt;li>SPI扩展。Dubbo的扩展能力得益于自己建的一套SPI机制。&lt;/li>
&lt;li>多实例。&lt;/li>
&lt;li>配置相关的内容。&lt;/li>
&lt;li>业务上自己用到的反射的行为。&lt;/li>
&lt;/ul>
&lt;p>第二,resource的元数据。如果业务上要做扩展,配置文件,resource的元数据主要涉及的就是META-INF下的三个路径。此外,还有一个是在Dubbo 3里支持对安全性的增强,序列化的黑白名单的resources的配置。&lt;/p>
&lt;p>第三,序列化的元数据。作为rpc调用的框架,它的接口定义、方法定义、内外部分的服务、parameter、请求参数、返回类型等都需要用到序列化的接口。&lt;/p>
&lt;p>第四,动态代理的元数据。在传统的rpc框架里动态代理是用的比较多的产品。内外部分服务的引用就是代理的元数据。&lt;/p>
&lt;p>第五,JNI的元数据,暂时没有用到。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_15.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>这是Dubbo 相关的 Reachability Metadata 总结和处理策略,我们把它分为了四类。&lt;/p>
&lt;ul>
&lt;li>规律性的内容,是刚刚提到的Adaptive Source Code的生成。&lt;/li>
&lt;li>确定性资源和行为,是Dubbo内部的扩展以及资源的配置。&lt;/li>
&lt;li>不确定性的资源和行为,是业务自定义的扩展实现以及定义的服务。&lt;/li>
&lt;li>集成和依赖的组件,比如刚刚提到jka是其他社区的生态,它涉及到注解的元数据信息,我们会提供官方的支持,还会提供内部的适配逻辑。&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_16.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>这是AOT的执行流程。可以看到它的源码在编译之后,它的区别是它会从main函数开始启动Java应用进程,然后去找所有的source code和Bean的元数据信息。&lt;/p>
&lt;p>下面是Spring Server自动生成的一个source code,它会把它生成一个Bean定义的获取的类。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_17.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>而Dubbo并不是从main函数启动的,它启动了一个扫描的进程,把确定性的、不确定性的元素以及规律性的内容扫描进来,自动帮大家生成元数据信息。&lt;/p>
&lt;p>下面是用Dubbo service生成后的一些信息。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_18.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>上图是Spring本身提供的一个产物的内容以及Dubbo的AOT产物的内容。可以看到Dubbo下面是Adaptive的一些source code。最后Native在执行的时候会读取到这里所有的配置。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_19.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>这是Dubbo和AOT之间的边界,可以看到API的接入方式和注解、XML的接入方式是有所区别的。注解和XML借用了Spring AOT的能力,包括ServiceBean、ReferenceBean等等,而Dubbo AOT的能力主要是自身的元数据的生产。&lt;/p>
&lt;h2 id="五dubbo-在-native-image-技术的未来规划">五、Dubbo 在 Native Image 技术的未来规划&lt;/h2>
&lt;ol>
&lt;li>提升开发者体验&amp;amp;开发效率&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_20.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>Dubbo在3.0之后提供了CTL、脚手架、IDEA插件,Dubbo Native Image目前还在建设中,之后也会加进去。此外,还有一些文档的建设。&lt;/p>
&lt;ol start="2">
&lt;li>性能优化与提升&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_21.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>刚刚已经分享了很多内容,但还是有很多可以做的事情。在GraalVM提供的能力上,我们还可以把一些类相关的可达性的配置加上去,产生作用之后能让最后打出来的二进制包更小,编译时间更短。&lt;/p>
&lt;ol start="3">
&lt;li>覆盖更多的组件&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-scripts/native/img_22.png" alt="dubbo-graalvm-native-image">&lt;/p>
&lt;p>因为目前很多组件都还不支持,所以我们现在的主要思路是把Dubbo主仓库的扩展性支持完成,然后再往wpi的扩展上做相应的支持。&lt;/p>
&lt;p>另外,内核所需要的可达性的元数据,我们会把它推到program的可达性的元数据的仓库上面去,让业务开发能够正常使用大陆内核里的元数据信息。&lt;/p>
&lt;p>最后我们的思路还是优先考虑GraalVM官方的支持。&lt;/p></description></item><item><title>Blog: 走向 Native 化:Spring&amp;Dubbo AOT 技术示例与原理讲解</title><link>https://dubbo.apache.org/zh-cn/blog/2023/06/28/%E8%B5%B0%E5%90%91-native-%E5%8C%96springdubbo-aot-%E6%8A%80%E6%9C%AF%E7%A4%BA%E4%BE%8B%E4%B8%8E%E5%8E%9F%E7%90%86%E8%AE%B2%E8%A7%A3/</link><pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/06/28/%E8%B5%B0%E5%90%91-native-%E5%8C%96springdubbo-aot-%E6%8A%80%E6%9C%AF%E7%A4%BA%E4%BE%8B%E4%B8%8E%E5%8E%9F%E7%90%86%E8%AE%B2%E8%A7%A3/</guid><description>
&lt;p>Java 应用在云计算时代面临“冷启动”慢、内存占用高、预热时间长等问题,无法很好的适应 Serverless 等云上部署模式,GraalVM 通过静态编译、打包等技术在很大程度上解决了这些问题,同时针对 GraalVM 的一些使用限制,Spring 和 Dubbo 等主流框架也都提供了相应的 AOT 解决方案。&lt;/p>
&lt;p>本文我们将详细分析 Java 应用在云时代面临的挑战,GraalVM Native Image 是如何解决这些问题,GraalVM 的基本概念与工作原理,最后我们通过一个 Spring6 + Dubbo3 的微服务应用示例演示了如何将一个普通微服务应用进行静态化打包。&lt;/p>
&lt;p>本文主要分为以下四个部分展开&lt;/p>
&lt;ol>
&lt;li>首先我们会先看一下在云计算快速发展的当下,云上应用应该具备的特点,Java 应用在云上所面临的挑战有哪些。&lt;/li>
&lt;li>其次,我会介绍一下 GraalVM,什么是 Native Image,如何通过 GraalVM 对 Java 应用进行静态化打出 Native Image 可执行的二进制程序。&lt;/li>
&lt;li>第三部分,我们知道 GraalVM 的使用是有一定限制的,比如 Java 的反射等动态特性是不被支持的,因此我们需要提供特殊的 Metadata 配置来绕过这些限制,在这一部分我们会讲解如何加入引入 AOT Processing 来实现自动化的 Metadata 配置,包括 Spring6 框架中 AOT 处理、Dubbo3 框架的 AOT 处理等。&lt;/li>
&lt;li>最后,我们将通过一个 Spring6+Dubbo3 的应用示例,来演示如何将这么一个 Java 应用进行静态化打包。&lt;/li>
&lt;/ol>
&lt;h2 id="java-应用在云时代所面临的挑战">Java 应用在云时代所面临的挑战&lt;/h2>
&lt;p>首先,我们先看一下云计算时代的应用特点,以及 Java 在云时代所面临的挑战。从各个统计机构给出的数据来看,Java 语言仍然是当今最受开发者欢迎的编程语言之一,仅次于一些脚本开发语言。使用 Java 语言可以非常高效的开发业务应用,丰富的生态使得 Java 具有非常高的开发和运行效率,有无数的应用基于 Java 语言开发。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/language-rank.png" alt="image.png">&lt;/p>
&lt;p>但在来到云计算时代,Java 应用的部署和运行开始面临非常多的问题。我们以Serverless为例,Serverless是云上的一种越来越主流的部署模式,它让开发者更专注业务逻辑、通过快速弹性等帮助解决资源问题,根据最新的一些数据,Java在所有云计算厂商的 Serverless 运行时中所占比例并不高,远远不能和它在传统应用开发中所占的比例相匹配。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/serverless-lang-rank.png" alt="image.png">&lt;/p>
&lt;p>出现这样的原因,主要是Java应用不能很好的满足Serverless场景的几个关键要求。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>首先是启动速度问题,Java 冷启动的耗时是比较长的&lt;/strong>。这对于Serverless需要快速弹起的场景是一个非常大的挑战,因为 Java 应用的拉起时间可能是秒、数十秒级别的;&lt;/li>
&lt;li>&lt;strong>第二点,Java应用往往都需要一定的预热时间,才能达到最佳的性能状态&lt;/strong>,刚刚拉起的应用如果分配比较大的流量是不合适的,往往会出现请求超时、资源占用过高等问题,这就进一步拉长了 Java 应用的有效拉起时间;&lt;/li>
&lt;li>&lt;strong>第三点是 Java 应用对运行环境的要求,它往往需要较大的内存、计算资源&lt;/strong>,而这些真正分配给业务自身的并不多,都消耗在一些JVM运行时上,这与用云降本提效的目标并不匹配;&lt;/li>
&lt;li>&lt;strong>最后,Java应用打出来的包或者镜像也是非常大&lt;/strong>,从总体上也影响存储、拉取的效率。&lt;/li>
&lt;/ul>
&lt;p>接下来,我们具体看一下针对 Java 应用所面临的这些问题, GraalVM 这样一种打包和运行时技术是如何解决的。&lt;/p>
&lt;h2 id="graalvm-简介">GraalVM 简介&lt;/h2>
&lt;blockquote>
&lt;p>GraalVM compiles your Java applications ahead of time into standalone binaries that start instantly, provide peak performance with no warmup, and use fewer resources.&lt;/p>
&lt;/blockquote>
&lt;p>引用官方介绍来看,GraalVM 为 Java 应用提供 AOT 编译和二进制打包能力,基于 GraalVM 打出的二进制包可以实现快速启动、具有超高性能、无需预热时间、同时需要非常少的资源消耗。这里所说的 AOT 是发生在编译期间的一个技术简称,即 Ahead-of-time,这一点我们后续会讲到。总的来说 GraalVM 可以分为两部分内容来看&lt;/p>
&lt;ul>
&lt;li>首先,&lt;strong>GraalVM 是一个完整的 JDK 发行版本&lt;/strong>,从这一点它是与 OpenJDK 对等的,可以运行任何面向jvm的语言开发的应用;&lt;/li>
&lt;li>其次,&lt;strong>GraalVM提供了 Native Image 打包技术&lt;/strong>,这可以将应用打包为可以独立运行的二进制包,这个包是自包含的、可脱离 JVM 运行的应用程序。&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/graalvm-compilation.png" alt="image.png">&lt;/p>
&lt;p>如上图所示,GraalVM 编译器提供了 JIT 和 AOT 两种模式。&lt;/p>
&lt;ul>
&lt;li>对于 JIT 而言,我们都知道Java类会被编译为 .class 格式的文件,这里编译后就是 jvm 识别的字节码,在 Java 应用运行的过程中,而 JIT 编译器又将一些热点路径上的字节码编译为机器码,已实现更快的执行速度;&lt;/li>
&lt;li>对于 AOT 模式来说,它直接在编译期间就将字节码转换为机器码,直接省去了运行时对jvm的依赖,由于省去了 jvm 加载和字节码运行期预热的时间,AOT 编译和打包的程序具有非常高的运行时效率。&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/graalvm-compilation2.png" alt="image.png">&lt;/p>
&lt;p>总的来说,JIT 使得应用可以具备更高的极限处理能力,可以降低请求的最大延迟这一关键指标;而 AOT 则可以进一步的提升应用的冷启动速度、具有更小的二进制包提及、在运行态需要更少的内存等资源。&lt;/p>
&lt;h2 id="什么是-native-image">什么是 Native Image?&lt;/h2>
&lt;p>我们上面多次提到 GraalVM 中 Native Image 概念,Native Image 是一项将 Java 代码编译打包为可执行二进制程序的技术,打出的包中仅包含运行期所需要的代码,包括应用自身代码、标准依赖包、 语言运行时、JDK 库关联的静态代码。这个包的运行不再需要 jvm 环境,当然它是和具体的机器环境相绑定的,需要为不同的机器环境单独打包。 Native Image 有这里列出来的一系列特点:&lt;/p>
&lt;ul>
&lt;li>仅包含 JVM 运行所需的一部分资源,运行成本更低&lt;/li>
&lt;li>毫秒级的启动时间&lt;/li>
&lt;li>启动后即进入最佳状态,无需预热&lt;/li>
&lt;li>可打包为更轻量的二进制包,让部署速度更快更高效&lt;/li>
&lt;li>安全程度更高&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/graalvm-advantages.png" alt="image.png">&lt;/p>
&lt;p>总结起来就是这里的关键几项:更快的启动速度、更少的资源占用、更小的安全漏洞风险、更紧凑的二进制包体积。解决 Java 应用在 Sererless 等云计算应用场景中面临的突出问题。&lt;/p>
&lt;h2 id="graalvm-native-image-的基本原理与使用">GraalVM Native Image 的基本原理与使用&lt;/h2>
&lt;p>接下来,我们看一下 GraalVM 的基本使用方式,首先,需要安装 native-image 需要的相关基础依赖,根据不同的操作系统环境会有所差异,接下来可以使用 GraalVM JDK 下载器下载 native-image。都安装好之后,第二步,就可以使用 native-image 命令编译和打包 Java 应用了,输入可以是 class 文件、jar文件、Java模块等,最终打包为一个可独立运行的可执行文件,比如这里的 HelloWorld。另外,GraalVM 也提供了对应的 Maven和Gradle构建工具插件,让打包过程更容易。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/graalvm-principal.png" alt="image.png">&lt;/p>
&lt;p>GraalVM 基于叫做 “closed world assumption” 即封闭世界假设的概念,要求在编译期间程序的所有运行时资源和行为即能被完全确定下来。图中是具体的 AOT 编译和打包过程,左侧应用代码、仓库、jdk等全部作为输入,GraalVM以 main 为入口,扫描所有可触达的代码与执行路径,在处理过程中可能会涉及到一些前置初始化动作,最终 AOT 编译的机器码和一些初始化资源等状态数据,被打包为可执行的 Native 包。&lt;/p>
&lt;p>相比于传统的 JVM 部署模式,GraalVM Native Image 模式带来的非常大的不同。&lt;/p>
&lt;ul>
&lt;li>GraalVM 在编译构建期间就会以 main 函数为入口,完成对应用代码的静态分析&lt;/li>
&lt;li>在静态分析期间无法被触达的代码,将会被移除,不会包含在最终的二进制包中&lt;/li>
&lt;li>GraalVM 无法识别代码中的一些动态调用行为,如反射、resource资源加载、序列化、动态代理等都动态行为都将受限&lt;/li>
&lt;li>Classpath 在构建阶段就固化下来,无法修改&lt;/li>
&lt;li>不再支持延迟的类加载,所有可用类和代码在程序启动阶段就确定了&lt;/li>
&lt;li>还有一些其他的 Java 应用能力是受限使用的(比如类初始化提前等)&lt;/li>
&lt;/ul>
&lt;p>GraalVM 不支持反射等动态特性,而我们的很多应用和框架中却大量使用了反射、动态代理等特性,如何才能将这些应用打包为 Native Image 实现静态化那? GraalVM 提供了元数据配置入口,通过为所有动态特性提供配置文件,“closed world assumption” 模式还是成立的,可以让 GraalVM 在编译期知道所有的预期行为。这里给了两个例子:&lt;/p>
&lt;ol>
&lt;li>编码方式上,比如这里反射的编码方式,可以让 GraalVM 通过代码分析计算 Metadata&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/metadata-1.png" alt="image.png">&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/metadata-2.png" alt="image.png">&lt;/p>
&lt;ol start="2">
&lt;li>另一个示例是提供额外的 json 配置文件并放在指定的目录 META-INF/native-image/&amp;lt;group.id&amp;gt;/&amp;lt;artifact.id&amp;gt; 下。&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/metadata-3.png" alt="image.png">&lt;/p>
&lt;h2 id="aot-processing">AOT Processing&lt;/h2>
&lt;p>Java 应用或框架中的反射等动态特性的使用是影响 GraalVM 使用的障碍,而大量的框架都存在这个限制,如果都要求应用或者开发者提供 Metadata 配置的话将会是一项非常有挑战的任务,因此,Spring 和 Dubbo 等框架都在 AOT Compilation 即 AOT 编译之前引入了 AOT Processing 即 AOT 预处理的过程,AOT Processing 用来完成自动化的 Metadata 采集,并将 Metadata 提供给 AOT 编译器使用。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/aot.png" alt="image.png">&lt;/p>
&lt;p>AOT 编译机制是对所有 Java 应用通用的,但相比于 AOT 编译,AOT Processing 采集 Metadata 的过程是每个框架都不同的,因为每个框架对于反射、动态代理等都有自己的用法。
我们以一个典型的 Spring + Dubbo 的微服务应用为例,要实现这个应用的静态化打包,这里涉及到 Spring、Dubbo 以及一众第三方依赖的 Metadata 处理过程。&lt;/p>
&lt;ul>
&lt;li>Spring - Spring AOT processing&lt;/li>
&lt;li>Dubbo - Dubbo AOT processing&lt;/li>
&lt;li>Third-party libraries - Reachability Metadata&lt;/li>
&lt;/ul>
&lt;p>对于 Spring 来说,Spring6 中发布了 Spring AOT 机制,用来支持 Spring 应用的静态化预处理;Dubbo 最近也在 3.2 版本中发布了 Dubbo AOT 机制,让 Dubbo 相关组件可以自动化实现 Native 预处理;除了这两个与业务开发密切相关的框架,一个应用中往往还有大量的第三方依赖,这些依赖的 Metadata 也是影响静态化的关键,如果它们中有反射、类加载等行为,那么需要为它们提供 Metadata 配置,对于这些第三方应用目前有两个渠道,一个是 GraalVM 官方提供的共享空间,这里有相当一部分依赖的 Metadata 配置可供使用(https://github.com/oracle/graalvm-reachability-metadata),另一种方式则是要求组件官方发布的发布中包含 Metadata 配置,对于这两种情况 GraalVM 都可以做到对于 Metadata 的自动读取。&lt;/p>
&lt;h3 id="spring-aot">Spring AOT&lt;/h3>
&lt;p>接下来我们看一下 Spring AOT 做了哪些编译之前的预处理工作,Spring 框架中有非常多的动态特性,比如自动配置、条件 Bean 等特性。Spring AOT 就是针对针对这些动态特性,在构建阶段进行预处理,生成可供 GraalVM 使用的一系列 Metadata 输入,这里生成的内容包括:&lt;/p>
&lt;ul>
&lt;li>Spring Bean 定义相关的代码预生成,如下图展示代码段&lt;/li>
&lt;li>在构建阶段生成动态代理相关代码&lt;/li>
&lt;li>关于一些反射等使用的 JSON 元数据文件&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/spring-aot.png" alt="image.png">&lt;/p>
&lt;h3 id="dubbo-aot">Dubbo AOT&lt;/h3>
&lt;p>Dubbo AOT 做的事情与 Spring AOT 非常类似,只不过 Dubbo AOT 是专门针对 Dubbo 框架特有的使用方式进行预处理,这包括:&lt;/p>
&lt;ul>
&lt;li>SPI 扩展相关的源代码生成&lt;/li>
&lt;li>一些反射使用的 JSON 配置文件生成&lt;/li>
&lt;li>RPC 代理类代码生成&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/dubbo-aot-1.png" alt="image.png">&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/dubbo-aot-2.png" alt="image.png">&lt;/p>
&lt;h2 id="spring6--dubbo3-示例演示">Spring6 + Dubbo3 示例演示&lt;/h2>
&lt;p>接下来,我们通过一个 Spring6 + Dubbo3 的示例微服务应用,演示如何使用 Spring AOT、Dubbo AOT 等,来实现应用的 Native Image 打包。&lt;/p>
&lt;p>完整的代码示例可在这里下载:&lt;a href="https://github.com/apache/dubbo-samples/tree/master/1-basic/dubbo-samples-native-image">dubbo-samples-native-image&lt;/a>&lt;/p>
&lt;h3 id="第一步安装graalvm">第一步:安装GraalVM&lt;/h3>
&lt;ol>
&lt;li>在Graalvm官网根据自己的系统选取对应Graalvm版本:&lt;a href="https://www.graalvm.org/downloads/">https://www.graalvm.org/downloads/&lt;/a>&lt;/li>
&lt;li>根据官方文档安装 native-image:&lt;a href="https://www.graalvm.org/latest/reference-manual/native-image/#install-native-image">Getting Started with Native Image&lt;/a>&lt;/li>
&lt;/ol>
&lt;h3 id="第二步创建项目">第二步:创建项目&lt;/h3>
&lt;p>这个示例应用就是普通的、常见的微服务应用,我们使用 SpringBoot3 进行应用配置开发,使用 Dubbo3 定义并发布 RPC 服务;应用构建工具使用 Maven。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/demo-1.png" alt="image.png">&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/demo-2.png" alt="image.png">&lt;/p>
&lt;h3 id="第三步配置-maven-插件">第三步:配置 Maven 插件&lt;/h3>
&lt;p>重点是增加 spring-boot-maven-plugin、native-maven-plugin、dubbo-maven-plugin 三个插件配置,开启 AOT 处理过程,修改dubbo-maven-plugin中的mainClass为所需的启动类全路径。(其中API使用方式无需添加spring-boot-maven-plugin依赖。)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;profiles&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;profile&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;id&amp;gt;&lt;/span>native&lt;span style="color:#268bd2">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;build&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;plugins&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>maven-compiler-plugin&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;release&amp;gt;&lt;/span>17&lt;span style="color:#268bd2">&amp;lt;/release&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;fork&amp;gt;&lt;/span>true&lt;span style="color:#268bd2">&amp;lt;/fork&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;verbose&amp;gt;&lt;/span>true&lt;span style="color:#268bd2">&amp;lt;/verbose&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.boot&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-maven-plugin&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;id&amp;gt;&lt;/span>process-aot&lt;span style="color:#268bd2">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goal&amp;gt;&lt;/span>process-aot&lt;span style="color:#268bd2">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.graalvm.buildtools&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>native-maven-plugin&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>0.9.20&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;classesDirectory&amp;gt;&lt;/span>${project.build.outputDirectory}&lt;span style="color:#268bd2">&amp;lt;/classesDirectory&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;metadataRepository&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;enabled&amp;gt;&lt;/span>true&lt;span style="color:#268bd2">&amp;lt;/enabled&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/metadataRepository&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;requiredVersion&amp;gt;&lt;/span>22.3&lt;span style="color:#268bd2">&amp;lt;/requiredVersion&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;id&amp;gt;&lt;/span>add-reachability-metadata&lt;span style="color:#268bd2">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goal&amp;gt;&lt;/span>add-reachability-metadata&lt;span style="color:#268bd2">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.apache.dubbo&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>dubbo-maven-plugin&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>${dubbo.version}&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;mainClass&amp;gt;&lt;/span>com.example.nativedemo.NativeDemoApplication&lt;span style="color:#268bd2">&amp;lt;/mainClass&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;phase&amp;gt;&lt;/span>process-sources&lt;span style="color:#268bd2">&amp;lt;/phase&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goal&amp;gt;&lt;/span>dubbo-process-aot&lt;span style="color:#268bd2">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/plugins&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/build&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/profile&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/profiles&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="第四步在pom依赖中添加native相关的依赖">第四步:在Pom依赖中添加native相关的依赖&lt;/h3>
&lt;p>另外,对于 Dubbo 而言,由于当前一些 Native 机制依赖 JDK17 等版本,Dubbo 没有将一些包默认打包到发行版本中,因此需要增加两个额外的依赖 dubbo-spring6 适配和 dubbo-native 组件。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.apache.dubbo&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>dubbo-config-spring6&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>${dubbo.version}&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.apache.dubbo&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>dubbo-native&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>${dubbo.version}&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="第五步调整compilerproxyserialization和logger">第五步:调整compiler、proxy、serialization和logger&lt;/h3>
&lt;p>同时,这个示例对于第三方组件的支持目前也是受限的,主要是第三方组件的 Reachability Metadata 。比如目前支持的网络通信或编码组件有 Netty 和 Fastjson2;支持的日志等组件为 Logback;微服务组件有 Nacos、Zookeeper 等。&lt;/p>
&lt;ul>
&lt;li>序列化方式目前支持的比较好的是Fastjson2&lt;/li>
&lt;li>compiler、proxy目前只能选择jdk&lt;/li>
&lt;li>logger目前需要配置slf4j,目前仅支持logback&lt;/li>
&lt;/ul>
&lt;p>示例配置如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">dubbo&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">application&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: ${spring.application.name}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">logger&lt;/span>: slf4j
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">port&lt;/span>: -&lt;span style="color:#2aa198">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">serialization&lt;/span>: fastjson2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registry&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">id&lt;/span>: zk-registry
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>: zookeeper://127.0.0.1:2181
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">config-center&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>: zookeeper://127.0.0.1:2181
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">metadata-report&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>: zookeeper://127.0.0.1:2181
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">provider&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">serialization&lt;/span>: fastjson2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">consumer&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">serialization&lt;/span>: fastjson2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="第六步编译">第六步:编译&lt;/h3>
&lt;p>在项目根路径下执行以下编译命令:&lt;/p>
&lt;ul>
&lt;li>API方式直接执行&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span> mvn clean install -P native -Dmaven.test.skip=true
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>注解和xml方式(Springboot3集成的方式)&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span> mvn clean install -P native native:compile -Dmaven.test.skip&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#b58900">true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="第七步执行二进制文件即可">第七步:执行二进制文件即可&lt;/h3>
&lt;p>二进制文件在 target/ 目录下,一般以工程名称为二进制包的名称,比如 target/native-demo&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>GraalVM 技术为 Java 在云计算时代的应用带来了新的变革,帮助解决了 Java 应用启动慢、资源占用,但同时我们也看到了 GraalVM 的使用也存在一些限制,因此 Spring6、SpringBoot3、Dubbo3 都提供了相应的 Native 解决方案。 Apache Dubbo 社区接下来将在周边生态组件等推进整体的 Native 静态化。&lt;/p></description></item></channel></rss>