Quarkus 是一个目前非常火的 Java 应用开发框架,定位是轻量级的微服务框架。Quarkus 提供了优秀的容器化整合能力,相较于传统开发框架(Spring Boot)有着更快的启动速度、更小的内存消耗、更短的服务响应。
通过 GraalVM 将 Quarkus 项目打包为 native运行程序,即可以实现项目的快速启动,达到秒级甚至为毫秒级。
但在使用过程中也同样有着一定的约束,比如——随机数。在原来将项目打包成jar的方式时,类的加载和初始化会在程序运行时才进行执行,比如java.util.Random类中的随机种子的生成会在程序真正运行并进行初始化时才会生成。但GraalVM 将 Quarkus 项目打包为 native运行程序时,会直接将类中的静态属性直接进行初始化操作,而不是等待程序运行时。
假如你恰巧在类的静态属性中定义并创建了java.util.Random对象
1 | private static final Random RANDOM = new Random(); |
那么你将会报如下错误:
1 | Error: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: Detected an instance of Random/SplittableRandom class in the image heap. Instances created during image generation have cached seed values and don't behave as expected. To see how this object got instantiated use --trace-object-instantiation=java.util.Random. The object was probably created by a class initializer and is reachable from a static field. You can request class initialization at image runtime by using the option --initialize-at-run-time=<class-name>. Or you can write your own initialization methods and call them explicitly from your main entry point. |
此时就是由于在GraalVM打包时会初始化Random对象和随机种子,同时又校验到不允许这样进行编码。
对于该问题的解决办法官方给了两种:
1、首先是他建议你修改代码逻辑,不要对类静态属性中的Random对象进行初始化。通过使用单例模式来实现对象的初始化和使用。
1 | public class RandomUtil { |
2、如果你不希望修改你的写法,或者出现问题的是第三方的jar包,你无法对源码进行修改,那么官方也提供了在运行时在进行初始化的方式。你只要指定需要在运行时才进行初始化的类,就可以解决这个问题。
在 application.properties 文件中增加配置:
1 | # 延迟初始化org.apache.commons.lang3.RandomStringUtils类 |
注意,在这里写的是包含Random静态属性的类,而不是Random类。
其实Quarkus官方自身也遇到了这个问题,而他们是通过编码的方式解决的,而不是通过参数配置。
Merge pull request #16008 from zakkak/grpc-random-reinit
所以在实际使用中你也可以有第三种解决办法,就是像官方一样通过编码来解决问题。
最后,感谢你的阅读。愿你的人生了无Bug。