我把Solon打包成了native image,速度快的惊人

news/2024/12/22 20:12:43/

我刚开始对 Solon 感兴趣的原因,就是启动快、包体积小,用了一段时间之后,发现 Solon 使用 GraalVM native iamge 打包有一些问题,我把问题发到 Solon 用户群里,作者告诉我 Solon 的原生编译还 beat 阶段,只做了一部分,问我有没有兴趣,然后我就一边研究 GraalVM naitve image 规范,一边看 Spring 的源码,看看前辈们是怎么实现的,到今天终于在 Solon 上有了阶段性的进展。



启动耗时13ms,内存占用13.5MB(不同机器启动时间会有所差异)

示例源码地址:github.com/dudiao/solo…

什么是 Solon AOT ?

AOT 是 Ahead-Of-Time 的简写,指运行前编译,与之对应的是 JIT,即 Just-In-Time,即时(动态)编译,边运行边编译。


Solon AOT 实际上是指 Solon AOT 优化,帮助 GraalVM 更好的将 Solon 应用编译为本机可执行程序(native image),大体思路是构建时,检查应用上下文,找到被使用的类、方法、字段等,并做出相应的决策。做到这些,需要在构建时启动应用,才能获取到上下文 AopContext,Solon AOT 在处理中,可能会生成:

  • Java 类(通常是动态代理)
  • RuntimeNativeMetadata 运行时元数据,包括反射,资源,序列化等
  • Solon 运行时元数据

使用方式

1. 增加solon.aot和solon.apt依赖

<!--solon native start-->
<!--aot 注册native元信息-->
<dependency><groupId>org.noear</groupId><artifactId>solon.aot</artifactId>
</dependency>
<!-- apt 生成代理类 -->
<dependency><groupId>org.noear</groupId><artifactId>solon.proxy.apt</artifactId><scope>provided</scope>
</dependency>
<!--solon native end-->
复制代码

2. 注册需要的运行时元数据(可选)

比如 User 类需要序列化,可以这样注册:

@Component
public class MyNativeRegistrar implements RuntimeNativeRegistrar {@Overridepublic void register(AopContext context, RuntimeNativeMetadata nativeMetadata) {nativeMetadata.registerSerialization(User.class);}
}
复制代码

实现RuntimeNativeRegistrar接口,且实现类需要是一个solon bean。

3. 生成为本机可执行程序(native image)

环境要求:graalvm 17 & native-image
如果你的项目中使用solon-parent来管理依赖,比如:

<parent><groupId>org.noear</groupId><artifactId>solon-parent</artifactId><version>2.2.13-SNAPSHOT</version><relativePath />
</parent><groupId>com.dudiao.solon</groupId>
<artifactId>solon-native-example</artifactId>
<version>1.0</version>
复制代码

直接使用如下命令打包为 native image

mvn clean native:compile -P native
复制代码

如果你的项目中没有使用solon-parent,可以在pom.xml中增加:

<profiles><profile><id>native</id><build><plugins><plugin><groupId>org.noear</groupId><artifactId>solon-maven-plugin</artifactId><version>${solon.version}</version><executions><execution><id>process-aot</id><goals><goal>process-aot</goal></goals></execution></executions></plugin><plugin><groupId>org.graalvm.buildtools</groupId><artifactId>native-maven-plugin</artifactId><version>${native.version}</version><!-- 使用graalvm提供的可达性元数据,很多第三方库就直接可以构建成可执行文件了 --><configuration><metadataRepository><enabled>true</enabled></metadataRepository></configuration><executions><execution><id>add-reachability-metadata</id><goals><goal>add-reachability-metadata</goal></goals></execution></executions></plugin></plugins></build></profile>
</profiles>
复制代码

同样使用mvn clean native:compile -P native即可打包为本机可执行程序。

也可以参考 solon native 的示例项目:
github.com/dudiao/solo…

实现原理


Solon AOT的总体思路是:

  1. 编译时,通过 apt 生成代理类(之后这部分逻辑会迁移到 solon-maven-plugin 中);
  2. Maven 构建时,增加ProcessAotMojo,用来收集应用的依赖包,通过 java -cp 命令调用SolonAotProcessor
  3. SolonAotProcessor中,会先反射执行应用主类(标记@SolonMain注解),获取到应用上下文;
  4. 注册AopContext中的 BeanWrap(应用中所有的bean)、MethodWrap(方法包装)到运行时元数据中;注册所有插件(PluginEntity)到元数据中;
  5. 用户手动注册到运行时元数据,实现 RuntimeNativeRegistrar 接口;
  6. 生成GraalVM Reachability Metadata(可达性元数据),包含:native-image.properties、resource-config.json、reflect-config.json、serialization-config.json。同时还会生成Solon元数据文件 solon-resource.json,用于在 native 环境中,扫描某个 resource 目录下的资源。

注意事项

通过静态编译构建的二进制程序,虽然有内存占用小,启动速度快的优点,但也有一些局限性,比如不能在运行时获取某个类的所有方法、获取所有 resource 资源。

Solon 正尝试另外一种方式来间接实现:通过在 AOT 阶段生成的元数据文件:reflect-config.jsonsolon-resource.json,运行时读取这两个文件,reflect-config.json包含了类和字段的信息,solon-resource.json中包含了resource目录下的资源信息。

可以通过工具类ReflectUtil获取类上所有字段和方法,工具类ScanUtil扫描路径下的所有资源。

最后

Solon + GraalVM native image 无论是启动速度,还有内存占用,都让我眼前一亮,如果你不了解 Solon,可以试一试。


http://www.ppmy.cn/news/48210.html

相关文章

内存管理、内存映射、mmap

内存管理 MMU&#xff1a;Memory Management Unit&#xff0c;内存管理单元&#xff0c;CPU中独立硬件&#xff0c;负责处理CPU的内存访问请求。虚拟地址到物理地址的转换&#xff08;即虚拟内存管理&#xff09;。 物理内存&#xff1a;真实存在的插在主板内存槽上的内存条&a…

C# 类库打包推送到nuget

步骤1&#xff1a;注册nuget 账号&#xff0c;可以使用outlook邮箱进行注册 步骤2&#xff1a;建立 apikey 名字自己起&#xff0c;Glob Pattern 填入“*” 步骤3&#xff1a;把程序打包&#xff0c;打包很简单右键vs2022 打包就好 但是注意*.csproj 文件修改,修改目的是为了…

python 操作CAD 二次开发 相关函数

import win32com.client as win32#输出dwg文件 from pyautocad import Autocad#输出dwg文件 import numpy as np#输出dwg文件 import pywin32 #输出dxf文件 import ezdxf #输出dxf文件 #打开CAD AutoCAD.Application.18 为 2010版本 #AutoCAD.Application.19 为 2014版本 #Au…

Python 使用pipreqs命令生成 `requirements.txt`报错

Python 使用pipreqs命令生成 requirements.txt报错&#xff1a;Fatal error in launcher: Unable to create process using ‘“E:\Anaconda\python.exe” “D:\Anaconda\Scripts\pipreqs.exe” ./ --encodingutf-8’: ??? 问题描述—Python 使用pipreqs命令生成 requireme…

MySQL_第09章_子查询

第09章_子查询 讲师&#xff1a;尚硅谷 - 宋红康&#xff08;江湖人称&#xff1a;康师傅&#xff09; 官网&#xff1a; http://www.atguigu.com 子查询指一个查询语句嵌套在另一个查询语句内部的查询&#xff0c;这个特性从 MySQL 4.1 开始引入。 SQL 中子查询的使用大大…

怎么设置动态壁纸?这样做就行!

案例&#xff1a;怎么设置动态壁纸 【朋友们&#xff0c;我的壁纸一直都是静态的&#xff0c;最近感觉有点审美疲劳了&#xff0c;想换些好看的动态壁纸&#xff0c;有朋友知道应该如何设置动态壁纸吗&#xff1f;】 经常使用电脑的朋友可能会觉得一直用同一张壁纸会感觉审美…

RabbitMQ之Work Queues

​ 工作队列(又称任务队列)的主要思想是避免立即执行资源密集型任务&#xff0c;而不得不等待它完成。 相反我们安排任务在之后执行。我们把任务封装为消息并将其发送到队列。在后台运行的工作进 程将弹出任务并最终执行作业。当有多个工作线程时&#xff0c;这些工作线程将一起…

IEEE14节点系统在如短路分析,潮流研究,互连电网中的研究(Simulink)

&#x1f4a5; &#x1f4a5; &#x1f49e; &#x1f49e; 欢迎来到本博客 ❤️ ❤️ &#x1f4a5; &#x1f4a5; &#x1f3c6; 博主优势&#xff1a; &#x1f31e; &#x1f31e; &#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 …