RocketMQ Graalvm 适配
发布时间 2024-01-27
GitHub issue 参见:https://github.com/alibaba/spring-cloud-alibaba/issues/3101
经验教训
- GraalVM Tracing Agent 收集到的信息可能不完整,所以依据这些信息编译出来的镜像运行时依然会报错,有时候需要手动补充
reflect-config.json
中的内容。
适配过程
- fastjson 需要升级到 fastjson2 才支持 GraalVM,GraalVM 下不能用字节码做优化,走的是反射。
- pom.xml 中需要增加
native-maven-plugin
这个插件并添加相应的配置
- 由于 RocketMQ 大量使用 fastjson ,其核心的几个类对象大量会被反射调用,因此需要在
reflect-config.json
配置文件中,针对这几个核心类对象增加如下配置
以下配置是给 rocketmq-broadcast-producer-example
这个用例中需要用到的。
以下配置是给 rocketmq-broadcast-consuemr1-example
这个用例中需要用到的。
解释如下
allDeclaredFields
代码所声明的对象中的所有成员变量都可以被反射调用,包括 public 和 protected private 的成员变量allPublicFields
代表所声明的对象中的所有 public 成员变量都可以被反射调用allPublicMethods
代码所声明的对象中所有的 public 方法都可以被反射调用allPublicConstructors
代码所声明的对象中所有的 public 的构造函数都可以被反射调用
以此类推。 当然,我们也可以声明某个具体的方法可以被反射调用,但是考虑到这几个核心对象的被反射的概率较大,而且方法和成员变量也不对,因此声明成所有都可以被反射。
graalvm-reachability-metadata
GraalVM 在编译的时候需要 reflect-json.config
等一系列的 hint 文件来编译 native-image,但通过 Tracing Agent 收集的方式可能会有遗漏,最好的方式是让每个中间件自己提供一份文件出来,这份文件中的 hint 信息是经过充分测试,这样也是最可靠的。最好的方式是中间件提供的 jar 里面就自带了这份 hint 文件,但这样要求所有中间件要重新发布一个新版本,针对老的版本如果提供这样的信息呢?graalvm 提供了这样一个机制,也就是 graalvm-reachability-metadata 把这些信息放在一个外部的仓库中,在编译的时候会从这个仓库中拉取所需要的编译信息。
此处演示如果通过本地仓库的方式添加这个信息,可以再插件配置中,主要是在 12 行处,加入本地仓库的地址
在这个仓库下创建如下目录格式
其中 第12行的 index.json
内容如下
org/apache/rocketmq/rocketmq-client/4.9.5-SNAPSHOT/index.json
内容如下:
其中比较核心的是 reflect-config.json
内容如下
这个内容是基于 Tracing Agent 收集的信息,加上手工补充的内容,形成的一个相对完整 hint 文件。GraalVM 在编译的时候会查找这个仓库,并使用这里面的信息编译 native image。在测试完成之后,可以提交到远程仓库中,https://github.com/oracle/graalvm-reachability-metadata
常见问题
如果运行时遇到以下异常:
说明 fastjson 运行的时候进行了反射,调用了 public org.apache.rocketmq.common.protocol.route.TopicRouteData()
这个方法,但是 Tracing Agent 并没有收集到这个方法,导致编译 native image 的时候并没有把这个方法允许进行反射,所以就报错了,此时需要修改 reflect-config.json
文件,讲该方法加入到运行反射的列表当中。