向云原生靠近 | 体验 GraalVM 静态编译字节码,尝试 “超声波 Java” - Quarkus
你身边的项目架构云原生吗,还是只是云却不原生?Java 能够像 C、Go 等语言一样做静态编译吗?如果能,会对现有架构造成什么样的影响?因 JVM 的短板,相关技术栈对于云原生架构来说不是一个很好的选择,难道 JVM 在云原生架构方面只能被后浪们蚕食吗?
本人资历、水平有限,如有错误请大佬们批评指正,谢谢!
你身边的项目架构云原生吗,还是只是云却不原生?
Java 能够像 C、Go 等语言一样做静态编译吗?如果能,会对现有架构造成什么样的影响?
因 JVM 的短板,相关技术栈对于云原生架构来说不是一个很好的选择,难道 JVM 在云原生架构方面只能被后浪们蚕食吗?
文章目录
- 前言
- 环境准备
- 初尝 GraalVM 的 Native Image
- 体验 Quarkus
- 参考资料
前言
基于 JVM 的应用启动比原生应用慢一拍
平时 Java 用得比较多,也会用 Golang 或者 C 等静态编译语言的人,应该多少都会感觉到,即使是最简单的 hello, world,Java 程序的启动速度相比静态编译出的可执行程序会慢上一拍。
如果使用 Java 等语言实现的运行在 JVM 上的 CLI 工具,这种慢一拍的感觉应该会更明显(使用过 RocketMQ
的 mqadmin
脚本的同学应该有所体会)。
Spring Framework
全家桶的启动就更不用说了,大项目启动时间可达 分钟 级别……
先来个简单的用户体验实验:
一段 Java 的 hello, world
public class Main {
public static void main(String[] args) {
System.out.println("hello, java");
}
}
一段 Golang 的 hello, world
package main
func main() {
println("hello, go")
}
➜ ~ go build main.go
➜ ~ repeat 10 time ./main
hello, go
./main 0.00s user 0.00s system 107% cpu 0.001 total
hello, go
./main 0.00s user 0.00s system 118% cpu 0.001 total
hello, go
./main 0.00s user 0.00s system 110% cpu 0.001 total
hello, go
./main 0.00s user 0.00s system 109% cpu 0.001 total
hello, go
./main 0.00s user 0.00s system 112% cpu 0.001 total
hello, go
./main 0.00s user 0.00s system 119% cpu 0.001 total
hello, go
./main 0.00s user 0.00s system 109% cpu 0.001 total
hello, go
./main 0.00s user 0.00s system 108% cpu 0.001 total
hello, go
./main 0.00s user 0.00s system 106% cpu 0.001 total
hello, go
./main 0.00s user 0.00s system 107% cpu 0.001 total
➜ ~ javac Main.java
➜ ~ repeat 10 time java Main
hello, java
java Main 0.07s user 0.04s system 119% cpu 0.087 total
hello, java
java Main 0.07s user 0.02s system 115% cpu 0.074 total
hello, java
java Main 0.06s user 0.01s system 114% cpu 0.063 total
hello, java
java Main 0.06s user 0.01s system 146% cpu 0.046 total
hello, java
java Main 0.06s user 0.01s system 147% cpu 0.049 total
hello, java
java Main 0.05s user 0.02s system 142% cpu 0.046 total
hello, java
java Main 0.05s user 0.01s system 143% cpu 0.045 total
hello, java
java Main 0.05s user 0.01s system 142% cpu 0.045 total
hello, java
java Main 0.05s user 0.02s system 144% cpu 0.045 total
hello, java
java Main 0.05s user 0.01s system 142% cpu 0.045 total
注意!以上测试 仅用于比较用户体验 ,不代表 Golang 与 Java 的性能好坏。
Golang 写的 hello, world 基本是 敲下键盘那一刻就已经运行完毕退出了,Java 写的 hello, world 人类可以感受到敲下键盘那一刻命令行的停滞,尤其是冷启动(首次启动 JVM 进程,可能长达数百毫秒。在树莓派这类硬件上,JVM 冷启动可长达数秒)。
运行期间性能不差,启动笨重一点又怎么样?
基于 JVM 的应用确实需要一定的启动时间(尤其是 Spring),启动内存相比原生应用也不低。“速度慢,吃内存” 成了大家黑 Java 的关键点。
不过,在目前常见的架构下,这些问题不会特别突出:
- 在应用运行期间,因 JVM 的各种优化手段,应用的运行性能会逐渐优化,不会比原生应用差。
- 在内存方面,JVM 所需内存对于一般服务器也没有什么压力。
- 启动慢的问题,在有滚动发布机制的环境中,应用多花一点时间进入就绪状态也没什么问题。
这么看来其实 “速度慢,吃内存” 也不是什么问题。
但是, 脱离场景谈技术都是耍流氓 。
对于一些用户群体比较确定、不会有突发流量,充分评估过资源需求、本身比较稳定的应用,启动时间和内存占用一般不会有什么问题。
但是对于弹性计算、存在突发流量等需要时刻准备水平扩容的场景,JVM 的短板就被放大了。
例如基于事件驱动的函数计算,按需分配资源,不用的时候不占用资源。不预留实例的函数计算,只有事件发生的时候才会分配资源并创建实例。
从以下三个方面谈 JVM 的短板:
-
在启动时间方面:
如果函数实例是后台计算等不涉及用户体验的操作,实例启动慢一点影响不会很大。
但如果函数提供的服务具有即时性、涉及用户体验、对时间敏感,实例启动时间就会在很大程度上影响着用户体验。原生应用和 JVM 相比,本身就没有启动的过程(本人的理解,从另一个角度说,操作系统的启动就是原生应用的启动)。因应用经过静态编译,业务逻辑的启动一般也不会占用太多时间(除非涉及 I/O 等对于 CPU 来说很慢的操作)。 -
在内存占用方面:
例如使用阿里云函数计算服务,内存需求关系到费用问题。一般虚拟机和原生应用相比在内存占用方面没有优势。一段相同逻辑的代码分别使用虚拟机和原生应用运行,虚拟机内存需求一般不会低于原生应用。 -
在运行期间:
JVM 有很多优化手段,基于 JVM 的应用经过时间的推移或者主动进行预热,可能会越来越快。但是,在弹性计算这类场景中,JVM 可能根本来不及优化,实例就已经被释放了。没有经过优化的 JVM 应用在运行速度上很难和原生应用的机器语言匹敌。
所以,回答标题的问题:
在现在这种新架构、新技术百花齐放的时代,如果 JVM 相关技术栈不能与时俱进,将很容易被后来者蚕食。
GraalVM - Run Programs Faster Anywhere
GraalVM 是 Oracle 实验室的黑科技之一。
和 Java 的 “一次编译,到处运行” 不同,GraalVM 是 “让程序在任何地方运行更快”。
特性:
- 增加应用程序的吞吐量、减少延迟
- 将应用程序编译为独立小巧的本地可执行二进制程序
- 无缝使用多种语言和库
篇幅有限,本文只关注 Native Image。除了 Native Image,GraalVM 还有很多黑科技。
官网:https://www.graalvm.org/
根据官网的下载页面,其实 GraalVM 就是基于 OpenJDK / Oracle JDK 构建的,并不是什么新的虚拟机,本身还是 HotSpot VM,现有的项目可以将虚拟机直接切换 GraalVM。
要说新研发的虚拟机,本人根据资料理解,构建好的原生应用中的 SubstrateVM
(SVM) 就是一个新的虚拟机(SVM 系列还包含了 AOT 等工具)。
Native Image,将字节码编译为真正的原生可执行程序
Native Image 可以在封闭环境下将一个基于 JVM 的应用通过 AOT 编译为二进制可执行程序或动态库。
这里的可执行程序并不是内嵌 JRE,而是真正的原生可执行程序。
编译器以字节码作为输入,因此支持各种基于 JVM 的语言。
Quarkus - Supersonic Subatomic Java
称为 “超声波 Java” 的 Quarkus 有什么特性?
CONTAINER FIRST(容器优先)
- Quarkus 完全支持 Graal / SubstrateVM:能够直接构建 Native Image 可执行程序
- Quarkus 会在构建时期尽量将不需要用到的类等可精简项排除掉,以实现更短的启动时间和更少的内存占用
- Quarkus 尽量避免使用反射(Reflection),减少启动时间和内存占用
- Quarkus 会尽量将一些启动工作放在 Native Image 构建期间完成,以减少启动时间
官方提供的 Quarkus原生应用、Quarkus JVM、传统云原生技术栈的内存占用、启动时间对比图:
UNIFIES IMPERATIVE AND REACTIVE(统一命令式和响应式编程)
无论是使用命令式还是响应式编程,都有相近的代码风格
命令式:
@Inject
SayService say;
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return say.hello();
}
响应式:
@Inject @Channel("kafka")
Publisher<String> reactiveSay;
@GET
@Produces(MediaType.SERVER_SENT_EVENTS)
public Publisher<String> stream() {
return reactiveSay;
}
DEVELOPER JOY(开发者友好)
- Quarkus 统一了各种不同的技术框架的配置
- Quarkus 在开发模式下可以实现保存代码即运行,而且开箱即用,无需额外配置
- Quarkus 支持各类构建工具,Maven, Gradle 或者 IDE
- Quarkus 将尽量统一开发的方式与风格
BEST OF BREED LIBRARIES AND STANDARDS(基于最好的库和标准)
Quarkus 不会让用户花费大量时间学习新技术,其编程模型建立在各种已有的标准之上,例如:
- JPA
- Spring
- JTA
- Vert.x
- CDI
- JAX-RS
- Apache Camel
- …
环境准备
本文环境概览
有条件的建议在 Linux 进行,Windows 上进行可能坑会稍多。
准备 GraalVM
GraalVM 可以在 Oracle 官网下载:https://www.oracle.com/downloads/graalvm-downloads.html?selected_tab=20l
注意版本的选择!
国内网络环境下载速度可能较慢,此处提供本人在用的版本:
- GraalVM EE Linux 20.1.0 https://wuweijie.oss-cn-shenzhen.aliyuncs.com/resources/graalvm-ee-java11-linux-amd64-20.1.0.tar.gz
- GraalVM EE Native Image Linux 20.1.0 组件离线安装包 (下载免C币) https://download.csdn.net/download/wu_weijie/12490465
GraalVM 的安装使用和普通的 JDK 没有区别,下载后解压并设置环境变量就可以了。
GraalVM 相比普通的 OpenJDK / Oracle JDK 增加了一些命令,例如用于管理 GraalVM 组件的命令 gu
。
不要忘记安装 Native Image
组件!可以通过 gu
命令安装提前下载好 Native Image
组件的 jar。
Native Image
组件安装文档:https://docs.oracle.com/en/graalvm/enterprise/20/guide/reference/native-image.html
➜ ~ javac -version
javac 11.0.7
➜ ~ java -version
java version "11.0.7" 2020-04-14 LTS
Java(TM) SE Runtime Environment GraalVM EE 20.1.0 (build 11.0.7+8-LTS-jvmci-20.1-b02)
Java HotSpot(TM) 64-Bit Server VM GraalVM EE 20.1.0 (build 11.0.7+8-LTS-jvmci-20.1-b02, mixed mode, sharing)
➜ ~ native-image
Please specify options for native-image building or use --help for more info.
环境基本准备好了!
使用 Native Image
可能还需要 gcc
等开发工具,各环境所需依赖可以参考 Quarkus 文档:
https://quarkus.io/guides/building-native-image#prerequisites
初尝 GraalVM 的 Native Image
hello, world 的 Native Image
native-image 使用方法
native-image
命令的帮助信息节选:
➜ hello-graalvm $GRAALVM_HOME/bin/native-image --help
GraalVM native-image building tool
This tool can be used to generate an image that contains ahead-of-time compiled Java code.
Usage: native-image [options] class [imagename] [options]
(to build an image for a class)
or native-image [options] -jar jarfile [imagename] [options]
(to build an image for a jar file)
可以看到,native-image
命令参数接受类的字节码文件或 jar ,我们直接将文章前言部分通过 javac
编译的 Main.class
拿来用。
构建 hello, world
此处写的是 Main
而不是 Main.class
,指定生成的可执行程序名为 main_native
。
➜ hello-graalvm $GRAALVM_HOME/bin/native-image Main main_native
Build on Server(pid: 29364, port: 34701)*
[main_native:29364] classlist: 987.61 ms, 0.96 GB
[main_native:29364] (cap): 590.98 ms, 0.96 GB
[main_native:29364] setup: 2,064.60 ms, 0.96 GB
[main_native:29364] (clinit): 99.22 ms, 1.21 GB
[main_native:29364] (typeflow): 3,109.82 ms, 1.21 GB
[main_native:29364] (objects): 3,299.24 ms, 1.21 GB
[main_native:29364] (features): 177.67 ms, 1.21 GB
[main_native:29364] analysis: 6,857.44 ms, 1.21 GB
[main_native:29364] universe: 199.91 ms, 1.21 GB
[main_native:29364] (parse): 518.11 ms, 1.21 GB
[main_native:29364] (inline): 1,130.87 ms, 1.66 GB
[main_native:29364] (compile): 7,976.54 ms, 3.15 GB
[main_native:29364] compile: 10,090.57 ms, 3.15 GB
[main_native:29364] image: 636.62 ms, 3.15 GB
[main_native:29364] write: 176.02 ms, 3.15 GB
[main_native:29364] [total]: 21,153.23 ms, 3.15 GB
以上是构建 Native Image
过程中的输出,4列信息分别代表:
- 目标
Native Image
名称与NativeImageBuildServer
的 Pid - 构建步骤
- 步骤耗时
NativeImageBuildServer
的堆大小(数值比top
命令监控到的内存稍小,本人猜测是堆大小)
构建完成后,目录中会有一个 main_native
可执行文件,重复执行 10 次:
➜ hello-graalvm ll ?ain*
-rwxr-xr-x 1 sia sia 1.2M 6月 2 22:30 main
-rw-r--r-- 1 sia sia 413 6月 2 23:18 Main.class
-rw-r--r-- 1 sia sia 51 6月 2 22:21 main.go
-rw-r--r-- 1 sia sia 104 6月 2 23:18 Main.java
-rwxr-xr-x 1 sia sia 8.1M 6月 6 10:53 main_native
➜ hello-graalvm repeat 10 time ./main_native
hello, java
./main_native 0.00s user 0.00s system 94% cpu 0.009 total
hello, java
./main_native 0.00s user 0.01s system 91% cpu 0.008 total
hello, java
./main_native 0.00s user 0.01s system 88% cpu 0.006 total
hello, java
./main_native 0.00s user 0.00s system 94% cpu 0.004 total
hello, java
./main_native 0.00s user 0.00s system 92% cpu 0.003 total
hello, java
./main_native 0.00s user 0.00s system 93% cpu 0.002 total
hello, java
./main_native 0.00s user 0.00s system 91% cpu 0.002 total
hello, java
./main_native 0.00s user 0.00s system 87% cpu 0.002 total
hello, java
./main_native 0.00s user 0.00s system 91% cpu 0.002 total
hello, java
./main_native 0.00s user 0.00s system 93% cpu 0.002 total
对比一下前言中的实验结果,native-image
构建出来的 “hello, world” 可执行程序启动速度明显比通过 JVM 运行快很多,也达到了 ”敲下键盘那一刻已经运行完毕并退出” 的境界。
关于 NativeImageBuildServer 与构建过程资源占用
➜ hello-graalvm jps -mlvV | grep 29364
29364 com.oracle.svm.hosted.server.NativeImageBuildServer -port=0 -logFile=/home/sia/.native-image/machine-id-ced39042e1ef44249ce6b3f25738a0e8/session-id-58f6/server-id-c54e3f0f73a1cc48d8cb58b05946a06b73cb841f608d5a7f9e62c46e06da98b438a2317d2bc2fcff22466f6a8846ce5cf572ac3413fb36f93e5238d3785da173/server.log -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCIProduct -XX:-UnlockExperimentalVMOptions -XX:ThreadPriorityPolicy=1 -XX:+UseParallelGC -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -Dtruffle.TrustAllTruffleRuntimeProviders=true -Dtruffle.TruffleRuntime=com.oracle.truffle.api.impl.DefaultTruffleRuntime -Dgraalvm.ForcePolyglotInvalid=true -Dgraalvm.locatorDisabled=true -Dsubstratevm.IgnoreGraalVersionCheck=true -Djava.lang.invoke.stringConcat=BC_SB --add-exports=jdk.internal.vm.ci/jdk.vm.ci.runtime=ALL-UNNAMED --add-exports=jdk.internal.vm.ci/jdk.vm.ci.code=ALL-UNNAMED --add-exports=jdk.internal.vm.ci/jdk.vm.ci.aarch64=ALL-UNNAMED --add-exports=jdk.internal.vm.ci/jdk.vm.ci.amd64=ALL-UNNAMED --add-exports=jdk.internal.vm.ci/jdk.vm.ci.meta=ALL-UNNAMED --add-exports=jdk.internal.vm.ci/jdk.vm.ci.hotspot=ALL-UNNAMED --add-exports=jdk.internal.vm.ci/jdk.vm.ci.services=ALL-UNNAMED --add-exports=jdk.internal.vm.ci/jdk.vm.ci.common=ALL-UNNAMED --add-exports=jdk.internal.vm.ci/jdk.vm.ci.code.site=ALL-UNNAMED --add-e
通过 jps
命令可以看到,native-image
会启动一个 NativeImageBuildServer
用于构建可执行程序。可执行程序构建完成后,该 Server 仍驻留,下次构建将使用该实例(再次构建同样的可执行程序耗时与内存 [main_native_2:29364] [total]: 14,191.21 ms, 4.55 GB
,耗时更少,但内存占用有所增加)
在另一个 Session 中启动的 top
命令,每 1 秒采集一次 graalvm 相关进程的运行状况。
➜ hello-graalvm top -d 1 -n60 -c | grep graalvm
29183 sia 20 0 32.264g 0.083g 0.014g S 27.6 0.1 0:00.29 /usr/local/graalvm-ee-java11-20.1.0/bin/native-i+
29364 sia 20 0 48.938g 0.078g 0.041g S 26.7 0.1 0:00.28 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 51.055g 0.546g 0.071g S 576.7 0.9 0:06.22 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 51.021g 0.638g 0.072g S 298.0 1.0 0:09.26 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 51.024g 0.663g 0.073g S 377.7 1.1 0:13.15 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29183 sia 20 0 32.264g 0.083g 0.014g S 1.0 0.1 0:00.30 /usr/local/graalvm-ee-java11-20.1.0/bin/native-i+
29364 sia 20 0 51.571g 0.802g 0.074g S 534.0 1.3 0:18.65 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 51.573g 0.856g 0.074g S 720.6 1.4 0:26.00 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 51.576g 0.922g 0.074g S 493.3 1.5 0:31.13 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 51.576g 0.923g 0.074g S 302.9 1.5 0:34.25 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 51.576g 0.988g 0.075g S 281.4 1.6 0:37.12 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 51.576g 1.055g 0.075g S 135.0 1.7 0:38.51 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 52.371g 1.147g 0.075g S 223.3 1.8 0:40.81 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 53.238g 1.311g 0.075g S 823.5 2.1 0:49.21 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 53.240g 1.315g 0.075g S 770.5 2.1 0:57.30 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 53.243g 1.660g 0.075g S 989.3 2.6 1:07.49 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 53.243g 1.762g 0.075g S 982.1 2.8 1:17.90 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 53.245g 1.864g 0.075g S 1024 3.0 1:28.75 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 53.246g 2.331g 0.075g S 1003 3.7 1:39.38 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 53.248g 2.337g 0.075g S 867.0 3.7 1:48.57 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 53.248g 2.366g 0.075g S 1030 3.8 1:59.18 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 53.250g 2.940g 0.075g S 917.3 4.7 2:09.27 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 53.252g 3.211g 0.075g S 516.5 5.1 2:14.59 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 53.155g 3.272g 0.075g S 408.7 5.2 2:18.80 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
29364 sia 20 0 53.155g 3.273g 0.075g S 0.9 5.2 2:18.81 /usr/local/graalvm-ee-java11-20.1.0/bin/java -cp+
第 9, 10 列分别是 CPU 和内存利用率。(CPU 利用率是每个逻辑核心利用率的和,理论上 12T 的 CPU 最高利用率为 1200)
本来我以为执行 native-image
就和 go build
差不多,没想到,本次构建的只是一个 hello, world,就已经占用了大量计算资源和近 3.3G 内存,这是一个不低的门槛。(剧透一下,Quarkus 构建可执行程序的占用资源会高很多)
Native Image 使用没有限制吗?肯定有
既然要做静态分析编译,肯定有所牺牲。
文档:https://github.com/oracle/graal/blob/master/substratevm/LIMITATIONS.md
动态类加载、反射、动态代理、JNI、JCA 等特性在部分场景需要配置
以反射为例:
Java 和 Go 都有反射 (Reflection),使用过的人应该清楚,Go 的反射能力相比 Java 的反射能力有所差距(例如 Go 不能直接办到 Java 的 Class.forName()
)。
如果构建 Native Image
,Class.forName()
等方法在一些场景下(例如从外部获取或动态编译代码等,无法在 native-image
构建期间分析的代码)需要提供配置文件指定一些信息。(这块资料较少)
配置相关资料:
- https://github.com/oracle/graal/blob/master/substratevm/CONFIGURE.md
- https://github.com/oracle/graal/blob/master/substratevm/REFLECTION.md
已知类的反射可以正常执行
来一个简单的示例,动态加载 java.lang.Integer
并获取 MAX_VALUE
的值:
NativeImageReflection.java
package vip.wuweijie.test.graalvm;
/**
* @author wuweijie
*/
public class NativeImageReflection {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
System.out.println(Class.forName("java.lang.Integer").getField("MAX_VALUE").get(null));
}
}
编译为 class 后用 java
直接运行:
➜ classes java vip.wuweijie.test.graalvm.NativeImageReflection
2147483647
构建可执行程序并运行:
➜ classes $GRAALVM_HOME/bin/native-image vip.wuweijie.test.graalvm.NativeImageReflection reflect
Build on Server(pid: 29364, port: 34701)
[reflect:29364] classlist: 90.08 ms, 5.13 GB
[reflect:29364] (cap): 280.83 ms, 5.13 GB
[reflect:29364] setup: 618.75 ms, 5.13 GB
[reflect:29364] (clinit): 102.93 ms, 5.13 GB
[reflect:29364] (typeflow): 2,906.36 ms, 5.13 GB
[reflect:29364] (objects): 3,788.48 ms, 5.13 GB
[reflect:29364] (features): 146.51 ms, 5.13 GB
[reflect:29364] analysis: 7,128.26 ms, 5.13 GB
[reflect:29364] universe: 185.85 ms, 5.13 GB
[reflect:29364] (parse): 318.00 ms, 5.13 GB
[reflect:29364] (inline): 447.50 ms, 5.13 GB
[reflect:29364] (compile): 4,994.34 ms, 5.20 GB
[reflect:29364] compile: 6,114.72 ms, 5.20 GB
[reflect:29364] image: 545.57 ms, 5.20 GB
[reflect:29364] write: 124.16 ms, 5.20 GB
[reflect:29364] [total]: 14,853.08 ms, 5.20 GB
➜ classes ./reflect
2147483647
封闭优化不支持的特性
invokedynamic(部分支持)
通过 javac
生成的 invokedynamic 仍然可以支持,例如 Lambda 和 String 拼接等。
Serialization(序列化,需要通过配置才能支持)
影响范围可能涉及到 java.rmi
。
Security Manager(安全管理)
建议使用独立进程(separate process)运行。
部分特性在 SVM 的实现会有所差异
Class Initializers
类初始化操作可以在构建 Native Image 时进行,也可以在应用启动时进行。有点高深,此处不展开。
不再调用 finalize() 方法
由于 finalize()
方法从 Java 9 开始废弃,SVM 将不会调用该方法,建议使用弱引用或引用队列代替 finalize()
方法。
SVM 未实现 java.lang.Thread 中的废弃方法
在 java.lang.Thread
类中,SVM 没有实现已经被废弃的方法,例如 Thread.stop()
等。
Unsafe 相关
有点高深,此处不展开。
尝试构建 Spring Boot 项目的 Native Image
默认参数构建出 Fallback Image
有一个 Spring Boot Eureka Server 的 jar,构建 Native Image:
➜ libs $GRAALVM_HOME/bin/native-image -jar eureka-server-1.0-SNAPSHOT.jar eureka-server
Build on Server(pid: 29364, port: 34701)
[eureka-server:29364] classlist: 107.58 ms, 5.12 GB
[eureka-server:29364] (cap): 308.11 ms, 5.12 GB
[eureka-server:29364] setup: 628.09 ms, 5.12 GB
[eureka-server:29364] (clinit): 127.87 ms, 5.12 GB
[eureka-server:29364] analysis: 13,088.76 ms, 5.12 GB
Warning: Aborting stand-alone image build due to unsupported features
Warning: Use -H:+ReportExceptionStackTraces to print stacktrace of underlying exception
Build on Server(pid: 29364, port: 34701)
[eureka-server:29364] classlist: 79.87 ms, 5.12 GB
[eureka-server:29364] (cap): 277.76 ms, 5.12 GB
[eureka-server:29364] setup: 556.38 ms, 5.12 GB
[eureka-server:29364] (clinit): 78.21 ms, 5.12 GB
[eureka-server:29364] (typeflow): 2,907.80 ms, 5.12 GB
[eureka-server:29364] (objects): 3,545.23 ms, 5.12 GB
[eureka-server:29364] (features): 70.02 ms, 5.12 GB
[eureka-server:29364] analysis: 6,753.20 ms, 5.12 GB
[eureka-server:29364] universe: 113.26 ms, 5.12 GB
[eureka-server:29364] (parse): 310.65 ms, 5.12 GB
[eureka-server:29364] (inline): 430.37 ms, 5.12 GB
[eureka-server:29364] (compile): 3,658.23 ms, 5.17 GB
[eureka-server:29364] compile: 4,703.36 ms, 5.17 GB
[eureka-server:29364] image: 406.92 ms, 5.17 GB
[eureka-server:29364] write: 150.86 ms, 5.17 GB
[eureka-server:29364] [total]: 12,805.75 ms, 5.17 GB
Warning: Image 'eureka-server' is a fallback image that requires a JDK for execution (use --no-fallback to suppress fallback image generation and to print more detailed information why a fallback image was necessary).
➜ libs ls
eureka-server eureka-server-1.0-SNAPSHOT.jar
➜ libs ./eureka-server
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.6.RELEASE)
2020-06-06 13:55:03.608 INFO 25975 --- [ main] v.w.p.e.server.EurekaServerApplication : No active profile set, falling back to default profiles: default
......
2020-06-06 13:55:06.926 INFO 25975 --- [ main] v.w.p.e.server.EurekaServerApplication : Started EurekaServerApplication in 4.201 seconds (JVM running for 4.758)
虽然构建出来可以运行,但是警告信息中也提醒了,这是一个 Fallback Image,仍然依赖 JVM 运行。
指定不使用 Fallback Image
如果通过 --no-fallback
指定不使用 Fallback Image,则构建过程中会报错:
➜ libs $GRAALVM_HOME/bin/native-image -jar eureka-server-1.0-SNAPSHOT.jar eureka-server --no-fallback
Build on Server(pid: 29364, port: 34701)
[eureka-server:29364] classlist: 84.36 ms, 5.18 GB
[eureka-server:29364] (cap): 255.60 ms, 5.18 GB
[eureka-server:29364] setup: 538.53 ms, 5.18 GB
[eureka-server:29364] (clinit): 123.71 ms, 5.18 GB
[eureka-server:29364] (typeflow): 5,010.03 ms, 5.18 GB
[eureka-server:29364] (objects): 7,302.70 ms, 5.18 GB
[eureka-server:29364] (features): 131.10 ms, 5.18 GB
[eureka-server:29364] analysis: 12,819.24 ms, 5.18 GB
Error: com.oracle.svm.hosted.substitute.DeletedElementException: Unsupported field java.net.URL.handlers is reachable
To diagnose the issue, you can add the option --report-unsupported-elements-at-runtime. The unsupported element is then reported at run time when it is accessed the first time.
Detailed message:
Trace:
at parsing java.net.URL.setURLStreamHandlerFactory(URL.java:1213)
Call path from entry point to java.net.URL.setURLStreamHandlerFactory(URLStreamHandlerFactory):
at java.net.URL.setURLStreamHandlerFactory(URL.java:1205)
at org.springframework.boot.loader.jar.JarFile.resetCachedUrlHandlers(JarFile.java:408)
at org.springframework.boot.loader.jar.JarFile.registerUrlProtocolHandler(JarFile.java:398)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:49)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:52)
at com.oracle.svm.core.JavaMainWrapper.runCore(JavaMainWrapper.java:149)
at com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:184)
at com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_5087f5482cc9a6abc971913ece43acb471d2631b(generated:0)
Error: Use -H:+ReportExceptionStackTraces to print stacktrace of underlying exception
Error: Image build request failed with exit status 1
指定运行时才报告错误
通过 --report-unsupported-elements-at-runtime
指定运行期间才抛出异常,则可以编译出可执行程序,但执行会报错:
➜ libs $GRAALVM_HOME/bin/native-image -jar eureka-server-1.0-SNAPSHOT.jar eureka-server --no-fallback --report-unsupported-elements-at-runtime
Build on Server(pid: 29364, port: 34701)
[eureka-server:29364] classlist: 88.63 ms, 5.18 GB
[eureka-server:29364] (cap): 263.34 ms, 5.18 GB
[eureka-server:29364] setup: 533.79 ms, 5.18 GB
[eureka-server:29364] (clinit): 165.56 ms, 5.18 GB
[eureka-server:29364] (typeflow): 5,307.18 ms, 5.18 GB
[eureka-server:29364] (objects): 7,254.71 ms, 5.18 GB
[eureka-server:29364] (features): 121.96 ms, 5.18 GB
[eureka-server:29364] analysis: 13,087.96 ms, 5.18 GB
[eureka-server:29364] universe: 223.50 ms, 5.18 GB
[eureka-server:29364] (parse): 418.09 ms, 5.18 GB
[eureka-server:29364] (inline): 679.36 ms, 5.12 GB
[eureka-server:29364] (compile): 6,067.78 ms, 5.19 GB
[eureka-server:29364] compile: 7,730.92 ms, 5.19 GB
[eureka-server:29364] image: 859.48 ms, 5.19 GB
[eureka-server:29364] write: 175.21 ms, 5.19 GB
[eureka-server:29364] [total]: 22,735.96 ms, 5.19 GB
➜ libs ./eureka-server
Exception in thread "main" java.lang.IllegalStateException: java.util.zip.ZipException: zip END header not found
at org.springframework.boot.loader.ExecutableArchiveLauncher.<init>(ExecutableArchiveLauncher.java:42)
at org.springframework.boot.loader.JarLauncher.<init>(JarLauncher.java:36)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:52)
Caused by: java.util.zip.ZipException: zip END header not found
at java.util.zip.ZipFile$Source.zerror(ZipFile.java:1535)
at java.util.zip.ZipFile$Source.findEND(ZipFile.java:1436)
at java.util.zip.ZipFile$Source.initCEN(ZipFile.java:1443)
at java.util.zip.ZipFile$Source.<init>(ZipFile.java:1274)
at java.util.zip.ZipFile$Source.get(ZipFile.java:1237)
at java.util.zip.ZipFile$CleanableResource.<init>(ZipFile.java:727)
at java.util.zip.ZipFile$CleanableResource.get(ZipFile.java:55)
at java.util.zip.ZipFile.<init>(ZipFile.java:247)
at java.util.zip.ZipFile.<init>(ZipFile.java:177)
at java.util.jar.JarFile.<init>(JarFile.java:348)
at java.util.jar.JarFile.<init>(JarFile.java:319)
at java.util.jar.JarFile.<init>(JarFile.java:285)
at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:119)
at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:114)
at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:100)
at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:91)
at org.springframework.boot.loader.archive.JarFileArchive.<init>(JarFileArchive.java:61)
at org.springframework.boot.loader.archive.JarFileArchive.<init>(JarFileArchive.java:57)
at org.springframework.boot.loader.Launcher.createArchive(Launcher.java:127)
at org.springframework.boot.loader.ExecutableArchiveLauncher.<init>(ExecutableArchiveLauncher.java:39)
... 2 more
压缩类的异常,本人判断是 Native Image 没有考虑到 Spring Boot 这种 “Jar 中 Jar” 的结构 。
基于 class 文件和指定 classpath 构建 Native Image
/usr/local/graalvm-ee-java11-20.1.0/bin/native-image vip.wuweijie.project.eureka.server.EurekaServerApplication -cp /home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/antlr-2.7.7.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/antlr-runtime-3.4.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/aopalliance-1.0.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/archaius-core-0.7.6.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/aspectjweaver-1.9.5.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/aws-java-sdk-autoscaling-1.11.415.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/aws-java-sdk-core-1.11.415.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/aws-java-sdk-ec2-1.11.415.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/aws-java-sdk-route53-1.11.415.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/aws-java-sdk-sts-1.11.415.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/bcpkix-jdk15on-1.64.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/bcprov-jdk15on-1.64.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/checker-compat-qual-2.5.5.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/classmate-1.5.1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/commons-codec-1.13.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/commons-collections-3.2.2.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/commons-configuration-1.8.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/commons-jxpath-1.3.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/commons-lang-2.6.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/commons-math-2.2.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/error_prone_annotations-2.3.4.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/eureka-client-1.9.17.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/eureka-core-1.9.17.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/evictor-1.0.0.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/failureaccess-1.0.1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/FastInfoset-1.2.16.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/freemarker-2.3.30.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/gson-2.8.6.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/guava-28.2-android.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/guice-4.1.0.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/HdrHistogram-2.1.11.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/hibernate-validator-6.0.18.Final.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/httpclient-4.5.12.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/httpcore-4.4.13.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/hystrix-core-1.5.18.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/ion-java-1.0.2.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/istack-commons-runtime-3.0.8.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/j2objc-annotations-1.3.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jackson-annotations-2.10.3.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jackson-core-2.10.3.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jackson-databind-2.10.3.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jackson-dataformat-cbor-2.10.3.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jackson-dataformat-xml-2.10.3.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jackson-datatype-jdk8-2.10.3.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jackson-datatype-jsr310-2.10.3.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jackson-module-jaxb-annotations-2.10.3.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jackson-module-parameter-names-2.10.3.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jakarta.activation-api-1.2.2.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jakarta.annotation-api-1.3.5.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jakarta.validation-api-2.0.2.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jakarta.xml.bind-api-2.3.3.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/javax.inject-1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jaxb-runtime-2.3.2.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jboss-logging-3.4.1.Final.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jersey-apache-client4-1.19.1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jersey-client-1.19.1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jersey-core-1.19.1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jersey-server-1.19.1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jersey-servlet-1.19.1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jettison-1.3.7.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jmespath-java-1.11.415.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/joda-time-2.10.5.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jsr305-3.0.2.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jsr311-api-1.1.1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/jul-to-slf4j-1.7.30.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/LatencyUtils-2.0.3.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/log4j-api-2.12.1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/log4j-to-slf4j-2.12.1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/logback-classic-1.2.3.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/logback-core-1.2.3.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/micrometer-core-1.3.6.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/netflix-commons-util-0.3.0.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/netflix-eventbus-0.3.0.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/netflix-infix-0.3.0.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/netflix-statistics-0.1.1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/reactive-streams-1.0.3.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/reactor-core-3.3.4.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/reactor-extra-3.3.3.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/ribbon-2.3.0.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/ribbon-core-2.3.0.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/ribbon-eureka-2.3.0.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/ribbon-httpclient-2.3.0.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/ribbon-loadbalancer-2.3.0.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/ribbon-transport-2.3.0.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/rxjava-1.3.8.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/rxnetty-0.4.9.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/rxnetty-contexts-0.4.9.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/rxnetty-servo-0.4.9.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/servo-core-0.12.21.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/slf4j-api-1.7.30.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/snakeyaml-1.25.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-aop-5.2.5.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-beans-5.2.5.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-boot-2.2.6.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-boot-actuator-2.2.6.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-boot-actuator-autoconfigure-2.2.6.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-boot-autoconfigure-2.2.6.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-boot-starter-2.2.6.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-boot-starter-actuator-2.2.6.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-boot-starter-aop-2.2.6.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-boot-starter-cache-2.2.6.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-boot-starter-freemarker-2.2.6.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-boot-starter-json-2.2.6.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-boot-starter-logging-2.2.6.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-boot-starter-tomcat-2.2.6.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-boot-starter-validation-2.2.6.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-boot-starter-web-2.2.6.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-cloud-commons-2.2.2.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-cloud-context-2.2.2.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-cloud-loadbalancer-2.2.2.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-cloud-netflix-archaius-2.2.2.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-cloud-netflix-eureka-client-2.2.2.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-cloud-netflix-eureka-server-2.2.2.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-cloud-netflix-hystrix-2.2.2.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-cloud-netflix-ribbon-2.2.2.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-cloud-starter-2.2.2.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-cloud-starter-loadbalancer-2.2.2.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-cloud-starter-netflix-archaius-2.2.2.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-cloud-starter-netflix-eureka-server-2.2.2.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-cloud-starter-netflix-ribbon-2.2.2.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-context-5.2.5.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-context-support-5.2.5.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-core-5.2.5.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-expression-5.2.5.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-jcl-5.2.5.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-security-crypto-5.2.2.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-security-rsa-1.0.9.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-web-5.2.5.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/spring-webmvc-5.2.5.RELEASE.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/stax2-api-4.2.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/stax-api-1.0.1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/stax-ex-1.8.1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/stringtemplate-3.2.1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/tomcat-embed-core-9.0.33.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/tomcat-embed-el-9.0.33.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/tomcat-embed-websocket-9.0.33.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/txw2-2.3.2.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/woodstox-core-6.1.1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/xmlpull-1.1.3.1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/xpp3_min-1.1.4c.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/libs/deps/xstream-1.4.11.1.jar:/home/sia/IdeaProjects/hello-world/eureka-server/build/classes/java/main --no-fallback --report-unsupported-elements-at-runtime
Shutdown Server(pid: 29364, port: 34701)
Build on Server(pid: 11904, port: 41579)*
[vip.wuweijie.project.eureka.server.eurekaserverapplication:11904] classlist: 11,353.59 ms, 2.69 GB
[vip.wuweijie.project.eureka.server.eurekaserverapplication:11904] (cap): 571.36 ms, 2.69 GB
[vip.wuweijie.project.eureka.server.eurekaserverapplication:11904] setup: 2,316.11 ms, 2.69 GB
[vip.wuweijie.project.eureka.server.eurekaserverapplication:11904] analysis: 7,777.10 ms, 2.70 GB
Error: Classes that should be initialized at run time got initialized during image building:
org.springframework.util.unit.DataSize was unintentionally initialized at build time. To see why org.springframework.util.unit.DataSize got initialized use -H:+TraceClassInitialization
Error: Use -H:+ReportExceptionStackTraces to print stacktrace of underlying exception
Error: Image build request failed with exit status 1
make: *** [build-native] Error 1
/home/sia/IdeaProjects/hello-world/eureka-server/Makefile:14: recipe for target 'build-native' failed
这就触及本人的知识盲区了,本文不再深入研究。
添加各项追踪参数的完整日志请见:https://paste.ubuntu.com/p/HZzyhR7bSQ/
体验 Quarkus
初始化 Quarkus 项目
Quarkus 提供了一个类似于 Spring Initializr 的项目初始化方式:https://code.quarkus.io/
任选一种初始化方式:
- 在 https://code.quarkus.io/ 完成初始化并下载项目
- 在 IntelliJ IDEA 创建 Quarkus 项目(推荐,封装了 code.quarkus.io)
以下操作基于 IntelliJ IDEA。
可选构建工具:
- Maven
- Gradle (Preview)
选择依赖:
- RESTEasy JAX-RS 默认选择
- 数据库相关
- Spring 相关(Preview 阶段,选来体验一下)
项目初始化后且依赖下载完成后,可以使用 ./mvnw compile quarkus:dev
运行项目,或者通过 GUI:
Quarkus 启动:
在浏览器打开 http://localhost:8080/
项目初始化完成。
数据库准备
create table t_order
(
id bigserial not null
constraint t_order_pk
primary key,
order_time timestamp default CURRENT_TIMESTAMP not null,
person_name varchar default ''::character varying not null,
order_type varchar default 'None'::character varying not null,
remark varchar
);
alter table t_order owner to postgres;
INSERT INTO public.t_order (id, order_time, person_name, order_type, remark) VALUES (1, '2020-06-06 16:31:16.000000', '烫烫烫', 'None', null);
INSERT INTO public.t_order (id, order_time, person_name, order_type, remark) VALUES (2, '2020-06-06 16:31:51.000000', '锟斤拷', 'Dinner', 'Double');
基于 Spring 体系的 CRUD 代码
Quarkus 提供了 Spring 的兼容。
Order.java
节选,忽略 getter / setter:
package vip.wuweijie.hello.quarkus.entity;
import javax.persistence.*;
import java.io.Serializable;
import java.sql.Timestamp;
/**
* @author wuweijie
*/
@Entity
public class Order implements Serializable {
@Id
private Long id;
@Column(insertable = false)
private Timestamp orderTime;
private String personName;
private String orderType;
private String remark;
}
OrderRepository.java
:
package vip.wuweijie.hello.quarkus.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import vip.wuweijie.hello.quarkus.entity.Order;
/**
* @author wuweijie
*/
public interface OrderRepository extends JpaRepository<Order, Long> {
}
OrderResource.java
package vip.wuweijie.hello.quarkus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import vip.wuweijie.hello.quarkus.entity.Order;
import vip.wuweijie.hello.quarkus.repository.OrderRepository;
import java.util.List;
/**
* @author wuweijie
*/
@RestController
@RequestMapping("/order")
public class OrderResource {
@Autowired
private OrderRepository orderRepository;
@GetMapping
public List<Order> getOrders() {
return orderRepository.findAll();
}
@PostMapping
public Order addOrder(@RequestBody Order order) {
return orderRepository.saveAndFlush(order);
}
}
在 Dev 模式运行验证
请求:
GET http://localhost:8080/order
###
POST http://localhost:8080/order
Content-Type: application/json
{
"orderTime": 1591432276000,
"personName": "POST",
"orderType": "nil",
"remark": "From Post"
}
先执行一次查询:
GET http://localhost:8080/order
HTTP/1.1 200 OK
Content-Length: 287
Content-Type: application/json
[
{
"id": 1,
"orderTime": 1591432276000,
"personName": "烫烫烫",
"orderType": "None",
"remark": null
},
{
"id": 2,
"orderTime": 1591432311000,
"personName": "锟斤拷",
"orderType": "Dinner",
"remark": "Double"
}
]
执行一次插入,返回数据:
{"id":3,"orderTime":1591432276000,"personName":"POST","orderType":"nil","remark":"From Post"}
再次查询数据:
GET http://localhost:8080/order
HTTP/1.1 200 OK
Content-Length: 287
Content-Type: application/json
[
{
"id": 1,
"orderTime": 1591432276000,
"personName": "烫烫烫",
"orderType": "None",
"remark": null
},
{
"id": 2,
"orderTime": 1591432311000,
"personName": "锟斤拷",
"orderType": "Dinner",
"remark": "Double"
},
{
"id": 3,
"orderTime": 1591434989319,
"personName": "POST",
"orderType": "nil",
"remark": "From Post"
}
]
Dev 模式下的 Hot Replace
如果检测到代码文件变化,则会重新加载代码:
2020-06-06 17:16:28,560 INFO [io.qua.dep.dev] (vert.x-worker-thread-1) Changed source files detected, recompiling [/home/sia/IdeaProjects/hello-quarkus/src/main/java/vip/wuweijie/hello/quarkus/entity/Order.java]
2020-06-06 17:16:28,950 INFO [io.quarkus] (Quarkus Main Thread) hello-quarkus stopped in 0.030s
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2020-06-06 17:16:29,130 INFO [io.qua.arc.pro.BeanProcessor] (build-26) Found unrecommended usage of private members (use package-private instead) in application beans:
- @Inject field vip.wuweijie.hello.quarkus.OrderResource#orderRepository
2020-06-06 17:16:29,220 INFO [io.agr.pool] (Quarkus Main Thread) Datasource '<default>': Initial size smaller than min. Connections will be created when necessary
2020-06-06 17:16:29,258 INFO [io.quarkus] (Quarkus Main Thread) hello-quarkus 1.0-SNAPSHOT on JVM (powered by Quarkus 1.5.0.Final) started in 0.301s. Listening on: http://0.0.0.0:8080
2020-06-06 17:16:29,259 INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2020-06-06 17:16:29,259 INFO [io.quarkus] (Quarkus Main Thread) Installed features: [agroal, cdi, hibernate-orm, hibernate-orm-panache, jdbc-postgresql, mutiny, narayana-jta, resteasy, resteasy-jackson, spring-boot-properties, spring-data-jpa, spring-di, spring-web]
2020-06-06 17:16:29,259 INFO [io.qua.dep.dev] (vert.x-worker-thread-1) Hot replace total time: 0.700s
构建 Native Image
本示例 建议可用内存不小于 8GB ,依赖越多,构建过程所需内存越大。
执行命令
./mvnw package -Pnative
执行日志:
➜ hello-quarkus git:(master) ✗ ./mvnw package -Pnative
[INFO] Scanning for projects...
[INFO]
[INFO] --------------< vip.wuweijie.hello.quarkus:hello-quarkus >--------------
[INFO] Building hello-quarkus 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-quarkus ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ hello-quarkus ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 4 source files to /home/sia/IdeaProjects/hello-quarkus/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello-quarkus ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/sia/IdeaProjects/hello-quarkus/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ hello-quarkus ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /home/sia/IdeaProjects/hello-quarkus/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ hello-quarkus ---
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running vip.wuweijie.hello.quarkus.ExampleResourceTest
2020-06-06 17:28:56,229 INFO [io.qua.arc.pro.BeanProcessor] (build-4) Found unrecommended usage of private members (use package-private instead) in application beans:
- @Inject field vip.wuweijie.hello.quarkus.OrderResource#orderRepository
2020-06-06 17:28:57,057 INFO [io.agr.pool] (main) Datasource '<default>': Initial size smaller than min. Connections will be created when necessary
2020-06-06 17:28:57,446 INFO [io.quarkus] (main) Quarkus 1.5.0.Final on JVM started in 2.301s. Listening on: http://0.0.0.0:8081
2020-06-06 17:28:57,447 INFO [io.quarkus] (main) Profile test activated.
2020-06-06 17:28:57,447 INFO [io.quarkus] (main) Installed features: [agroal, cdi, hibernate-orm, hibernate-orm-panache, jdbc-postgresql, mutiny, narayana-jta, resteasy, resteasy-jackson, spring-boot-properties, spring-data-jpa, spring-di, spring-web]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.775 s - in vip.wuweijie.hello.quarkus.ExampleResourceTest
2020-06-06 17:28:58,615 INFO [io.quarkus] (main) Quarkus stopped in 0.034s
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ hello-quarkus ---
[INFO] Building jar: /home/sia/IdeaProjects/hello-quarkus/target/hello-quarkus-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- quarkus-maven-plugin:1.5.0.Final:build (default) @ hello-quarkus ---
[INFO] [org.jboss.threads] JBoss Threads version 3.1.1.Final
[INFO] [org.hibernate.Version] HHH000412: Hibernate ORM core version 5.4.16.Final
[INFO] [io.quarkus.arc.processor.BeanProcessor] Found unrecommended usage of private members (use package-private instead) in application beans:
- @Inject field vip.wuweijie.hello.quarkus.OrderResource#orderRepository
[INFO] [io.quarkus.deployment.pkg.steps.JarResultBuildStep] Building native image source jar: /home/sia/IdeaProjects/hello-quarkus/target/hello-quarkus-1.0-SNAPSHOT-native-image-source-jar/hello-quarkus-1.0-SNAPSHOT-runner.jar
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Building native image from /home/sia/IdeaProjects/hello-quarkus/target/hello-quarkus-1.0-SNAPSHOT-native-image-source-jar/hello-quarkus-1.0-SNAPSHOT-runner.jar
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Running Quarkus native-image plugin on GraalVM Version 20.1.0 (Java Version 11.0.7)
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] /usr/local/graalvm-ee-java11-20.1.0/bin/native-image -J-Dsun.nio.ch.maxUpdateArraySize=100 -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager -J-DCoordinatorEnvironmentBean.transactionStatusManagerEnable=false -J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory -J-Dvertx.disableDnsResolver=true -J-Dio.netty.leakDetection.level=DISABLED -J-Dio.netty.allocator.maxOrder=1 -J-Duser.language=en -J-Dfile.encoding=UTF-8 --initialize-at-build-time= -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy$BySpaceAndTime -H:+JNI -jar hello-quarkus-1.0-SNAPSHOT-runner.jar -H:FallbackThreshold=0 -H:+ReportExceptionStackTraces -H:-AddAllCharsets -H:-IncludeAllTimeZones -H:EnableURLProtocols=http,https --enable-all-security-services -H:NativeLinkerOption=-no-pie --no-server -H:-UseServiceLoaderFeature -H:+StackTrace hello-quarkus-1.0-SNAPSHOT-runner
-H:IncludeAllTimeZones and -H:IncludeTimeZones are now deprecated. Native-image includes all timezonesby default.
[hello-quarkus-1.0-SNAPSHOT-runner:9103] classlist: 6,932.73 ms, 1.19 GB
[hello-quarkus-1.0-SNAPSHOT-runner:9103] (cap): 541.19 ms, 1.68 GB
[hello-quarkus-1.0-SNAPSHOT-runner:9103] setup: 2,467.68 ms, 1.68 GB
17:29:11,362 INFO [org.hib.Version] HHH000412: Hibernate ORM core version 5.4.16.Final
17:29:11,367 INFO [org.hib.ann.com.Version] HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
17:29:11,384 INFO [org.hib.dia.Dialect] HHH000400: Using dialect: io.quarkus.hibernate.orm.runtime.dialect.QuarkusPostgreSQL10Dialect
17:29:23,231 INFO [org.jbo.threads] JBoss Threads version 3.1.1.Final
WARNING GR-10238: VarHandle for static field is currently not fully supported. Static field private static volatile java.lang.System$Logger jdk.internal.event.EventHelper.securityLogger is not properly marked for Unsafe access!
[hello-quarkus-1.0-SNAPSHOT-runner:9103] (clinit): 873.00 ms, 5.41 GB
[hello-quarkus-1.0-SNAPSHOT-runner:9103] (typeflow): 21,272.04 ms, 5.41 GB
[hello-quarkus-1.0-SNAPSHOT-runner:9103] (objects): 26,923.87 ms, 5.41 GB
[hello-quarkus-1.0-SNAPSHOT-runner:9103] (features): 1,099.55 ms, 5.41 GB
[hello-quarkus-1.0-SNAPSHOT-runner:9103] analysis: 53,824.27 ms, 5.41 GB
[hello-quarkus-1.0-SNAPSHOT-runner:9103] universe: 1,649.18 ms, 5.41 GB
[hello-quarkus-1.0-SNAPSHOT-runner:9103] (parse): 4,248.05 ms, 5.59 GB
[hello-quarkus-1.0-SNAPSHOT-runner:9103] (inline): 2,953.33 ms, 5.79 GB
[hello-quarkus-1.0-SNAPSHOT-runner:9103] (compile): 61,356.01 ms, 7.23 GB
[hello-quarkus-1.0-SNAPSHOT-runner:9103] compile: 72,475.76 ms, 7.23 GB
[hello-quarkus-1.0-SNAPSHOT-runner:9103] image: 5,032.36 ms, 7.23 GB
[hello-quarkus-1.0-SNAPSHOT-runner:9103] write: 995.36 ms, 7.23 GB
[hello-quarkus-1.0-SNAPSHOT-runner:9103] [total]: 143,635.77 ms, 7.23 GB
[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 145589ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:34 min
[INFO] Finished at: 2020-06-06T17:31:25+08:00
[INFO] ------------------------------------------------------------------------
构建完成后,target 目录下多了一个可执行文件。
执行后,可以看到应用的启动时间只需要 0.043s,基于 JVM 启动需要 1.730s。
以上就是 Quarkus 的体验过程。
Demo 项目源码:https://github.com/TeslaCN/hello-quarkus
参考资料
GraalVM 相关:
- https://docs.oracle.com/en/graalvm/enterprise/20/guide/index.html
- https://github.com/oracle/graal/blob/master/substratevm/LIMITATIONS.md
- https://github.com/oracle/graal/blob/master/substratevm/CONFIGURE.md
- https://github.com/oracle/graal/blob/master/substratevm/REFLECTION.md
Quarkus 相关:
更多推荐
所有评论(0)