聊聊 Java 与原生,尝尝 GraalVM 与 Quarkus | 向云原生靠近
本人资历、水平有限,如有错误请大佬们批评指正,谢谢!
文章目录
- 前言
- 基于 JVM 的应用启动比原生应用慢一拍
- 运行期间性能不差,启动笨重一点又怎么样?
- 环境准备
- 本文环境概览
- 准备 GraalVM
- 初尝 GraalVM 的 Native Image
- GraalVM 这名字听起来像是新研发的虚拟机?
- hello, world 的 Native Image
- native-image 使用方法
- 构建 hello, world
- 关于 NativeImageBuildServer 与构建过程资源占用
- Native Image 使用没有限制吗?肯定有
- 动态类加载、反射、动态代理、JNI、JCA 等特性在部分场景需要配置
- 已知类的反射可以正常执行
- 封闭优化不支持的特性
- invokedynamic(部分支持)
- Serialization(序列化,需要通过配置才能支持)
- Security Manager(安全管理)
- 部分特性在 SVM 的实现会有所差异
- Class Initializers
- 不再调用 finalize() 方法
- SVM 未实现 java.lang.Thread 中的废弃方法
- Unsafe 相关
- 尝试构建 Spring Boot 项目的 Native Image
- 默认参数构建出 Fallback Image
- 指定不使用 Fallback Image
- 指定运行时才报告错误
- 基于 class 文件和指定 classpath 构建 Native Image
- Quarkus - Supersonic Subatomic Java
- 称为 “超声波 Java” 的 Quarkus 有什么特性?
- CONTAINER FIRST(容器优先)
- UNIFIES IMPERATIVE AND REACTIVE(统一命令式和响应式编程)
- DEVELOPER JOY(开发者友好)
- BEST OF BREED LIBRARIES AND STANDARDS(基于最好的库和标准)
- 说这么多,不如试试
- 初始化 Quarkus 项目
- 数据库准备
- 基于 Spring 体系的 CRUD 代码
- 在 Dev 模式运行验证
- dev 模式下支持 Hot Reload
- 构建 Native Image
- 参考资料
前言
基于 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 写的 hello, world 基本是 敲下键盘那一刻就已经运行完毕退出了,Java 写的 hello, world 运行时间人类可以感受到,尤其是冷启动(首次启动 JVM 进程,可能长达数百毫秒。在树莓派这类硬件上,JVM 冷启动可长达数秒)。
运行期间性能不差,启动笨重一点又怎么样?
基于 JVM 的应用确实需要一定的启动时间(尤其是 Spring),启动内存相比原生应用也不低。“速度慢,吃内存” 成了大家黑 Java 的关键点。
不过,在目前常见的架构下,这些问题不会特别突出:
- 在应用运行期间,因 JVM 的各种优化手段,应用的运行性能会逐渐优化,不会比原生应用差。
- 在内存方面,JVM 所需内存对于一般服务器也没有什么压力。
- 启动慢的问题,在有滚动发布机制的环境中,应用多花一点时间进入就绪状态也没什么问题。
这么看来其实 “速度慢,吃内存” 也不是什么问题。
但是, 脱离场景谈技术都是耍流氓 。
对于一些用户群体比较确定、不会有突发流量,充分评估过资源需求、本身比较稳定的应用,启动时间和内存占用一般不会有什么问题。
但是对于弹性计算、存在突发流量等需要时刻准备水平扩容的场景,JVM 的短板就被放大了。
例如基于事件驱动的函数计算,按需分配资源,不用的时候不占用资源。不预留实例的函数计算,只有事件发生的时候才会分配资源并创建实例。
从以下三个方面谈 JVM 的短板:
-
在启动时间方面:
如果函数实例是后台计算等不涉及用户体验的操作,实例启动慢一点影响不会很大。
但如果函数提供的服务具有即时性、涉及用户体验、对时间敏感,实例启动时间就会在很大程度上影响着用户体验。原生应用和 JVM 相比,本身就没有启动的过程(本人的理解,从另一个角度说,操作系统的启动就是原生应用的启动)。因应用经过静态编译,业务逻辑的启动一般也不会占用太多时间(除非涉及 I/O 等对于 CPU 来说很慢的操作)。 -
在内存占用方面:
例如使用阿里云函数计算服务,内存需求关系到费用问题。一般虚拟机和原生应用相比在内存占用方面没有优势。一段相同逻辑的代码分别使用虚拟机和原生应用运行,虚拟机内存需求一般不会低于原生应用。 -
在运行期间:
JVM 有很多优化手段,基于 JVM 的应用经过时间的推移或者主动进行预热,可能会越来越快。但是,在弹性计算这类场景中,JVM 可能根本来不及优化,实例就已经被释放了。没有经过优化的 JVM 应用在运行速度上很难和原生应用的机器语言匹敌。
所以,回答标题的问题:
在现在这种新架构、新技术百花齐放的时代,如果 JVM 相关技术栈不能与时俱进,将很容易被后来者蚕食。
环境准备
本文环境概览
有条件的建议在 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
除了 Native Image,GraalVM 还有很多黑科技。篇幅有限,本文只关注 Native Image。
官网:https://www.graalvm.org/
GraalVM 这名字听起来像是新研发的虚拟机?
根据官网的下载页面,其实 GraalVM 就是基于 OpenJDK / Oracle JDK 构建的,并不是什么新的虚拟机,本身还是 HotSpot VM,现有的项目可以将虚拟机直接切换 GraalVM。
要说新研发的虚拟机,本人根据资料理解,构建好的原生应用中的 SubstrateVM
(SVM) 就是一个新的虚拟机(SVM 系列还包含了 AOT 等工具)。
hello, world 的 Native Image
native-image 使用方法
native-image
命令的帮助信息节选:
➜ hello-graalvm $GRAALVM_HOME/bin/native-image --helpGraalVM native-image building toolThis 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 foundat 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 foundat 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:+TraceClassInitializationError: 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 - 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
- …
说这么多,不如试试
初始化 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 nullconstraint t_order_pkprimary 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 {@Idprivate 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 {@Autowiredprivate OrderRepository orderRepository;@GetMappingpublic List<Order> getOrders() {return orderRepository.findAll();}@PostMappingpublic 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/orderHTTP/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/orderHTTP/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 Reload
如果检测到代码文件变化,则会重新加载代码:
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 的体验过程。
参考资料
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 相关:
- https://quarkus.io/assets/images/quarkus_metrics_graphic_bootmem_wide.png
- https://quarkus.io/
- https://quarkus.io/vision/container-first
- https://quarkus.io/vision/standards
- https://quarkus.io/vision/developer-joy
- https://quarkus.io/vision/continuum
- https://quarkus.io/guides/building-native-image
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
相关文章
- 慕课网项目 校园商铺项目01-------------准备工作
准备工作环境准备创建项目解决报错解决警告新建Source Folder更改Dynamic Web Module版本 环境准备 1.Eclipse 2.Maven3.5.0 3.Tomcat8.5 4.JDK8 5.Mysql5.5 创建项目 解决报错 新建maven project——选择maven-archetype-webapp——一路点击next完成项目创建创建完成后会发现项…...
2024/4/16 3:28:18 - 我的工具箱
命令行工具 – cmder支持右键新建一个新的命令行窗口、这不香 ? 这么好看的界面、不香?各种提示、不香? Kafka Tool可以看到集群中的所有 broker , topic , consumer , 可以看到堆积情况、消息的 key、value 。支持多个平台(这个很重要) Jetbrains 全家桶之 – IntelliJ IDE…...
2024/4/20 18:11:45 - freertos任务调度器
任务调度器初始化过程: 1.创建空闲任务 /* The Idle task is being created using dynamically allocated RAM. */ xReturn = xTaskCreate( prvIdleTask, “IDLE”, configMINIMAL_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), &xIdleTaskHan…...
2024/4/16 3:29:09 - 随笔-数据库-约束
目录1、什么是约束?2、约束特点3、创建约束3.1、建表时创建列级约束3.2、建表时创建表级约束3.3、建表后加约束4、约束命名4.1、命名原则4.1、列级约束命名4.2、表级约束命名4.3、建表后加约束5、约束操作1、什么是约束?约束是一种自动保持数据库完整性的一种方法,定义了数据…...
2024/4/3 2:31:16 - 对3d坐标和四元数做线性插值计算
功能介绍 设位姿点PPP的向量为(t,x,y,z,qx,qy,qz,qw)(t,x,y,z,qx,qy,qz,qw)(t,x,y,z,qx,qy,qz,qw), 其中 (t)(t)(t)代表时间戳,(x,y,z)(x,y,z)(x,y,z)代表3d3d3d位置坐标,(qx,qy,qz,qw)(qx,qy,qz,qw)(qx,qy,qz,qw)代表姿态四元数, 所要求的是按照指定的时间间隔,在两个位姿…...
2024/4/16 3:28:23 - SQL SERVER(46)图书管理系统之一数据库设计
创建一个数据库bookmanager添加两个数据表图书信息表用户表打开VB,创建一个Windows应用程序添加数据源找到刚创建的数据库bookmanager,添加到项目里创建一个PubFunction类Imports System.Data.SqlClient Public Class PubFunctionPublic Function GetConnection() As SqlConn…...
2024/4/15 5:11:37 - JDK8下载安装教程
JDK8下载安装教程 下载 1、地址栏直接输入https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html 或者直接百度JDK8 download后点击第一个链接进入,如下图:2、向下滑动鼠标,选择自己需要的版本即可,我这里选择的windows64,如下图:3、勾选同意协议,…...
2024/4/15 5:11:36 - C++大小写转换作业中发现对于cout char输出结果的不同
字母大小写转换的程序:```cpp #include<iostream> using namespace std; int trans(char a) {return a - 32; } void main() {char a;cin >> a;a = trans(a);cout << a;//如果直接cout<<trans(a)的话,最终输出的结果是数值system("pause")…...
2024/4/15 5:11:35 - html页面中if语句的写法
html页面中if语句的写法...
2024/4/18 3:29:02 - Flutter Mixins(with)
混入 mixins (关键字 with) Flutter 使用的是 Dart 支持 Mixin ,而 Mixin 能够更好的解决多继承中容易出现的问题,如:方法优先顺序混乱、参数冲突、类结构变得复杂化等等。 混入的原则是:相同方法被覆盖了,并且 with 后面的会覆盖前面的。 下面代码 一目了然 main(){pri…...
2024/4/15 5:11:33 - Java语法学习-对象和类
Java基础-关键字double intabstract superassert elseinterface switchboolean enumlong synchronizedbreak extendsnative thisbyte finalnew throwfinally packagethrows floatcase catchprivate transientchar forprotected tryclass gotopublic voidconst ifreturn volatil…...
2024/4/15 5:11:32 - lodash源码学习-数组篇
1,chunkfunction slice(array, start, end) {// 1, 对参数进行处理let length = array == null ? 0 : array.lengthif (!length) {return []}// 1.1 对开始参数进行初步处理start = start == null ? 0 : startend = end === undefined ? length : end// 1.2 对开始参数再次…...
2024/4/15 5:11:31 - 排序算法的复杂度
...
2024/4/15 5:11:30 - (Linux-OS实验)信号量与共享内存
信号量与共享内存 求100000个浮点数的和。要求: (1)随机生成100000个浮点数(父进程); (2)然后创建4个后代进程,分别求25000个浮点数的和; (3)父进程对4个后代进程的结果求和; (4)进程间用共享内存进行通信,用信号量实现同步和互斥。要求在创建子进程之后,由父…...
2024/4/16 3:27:53 - C语言模拟实现库函数strlen(递归和非递归实现)
非递归实现: #include<stdio.h> #include<windows.h> int MyStrlen(char* str) {//模拟实现strlen函数int count = 0;while (*str != \0){count++;str++;}return count; } int main() {char arr[] = "abcd1235";printf("%d\n", MyStrlen(arr)…...
2024/4/16 3:28:13 - Typescript03:函数全解
函数的标注 一个函数的标注包含参数 返回值function fn(a: string): string {}; let fn: (a: string) => string = function(a) {};type callback = (a: string): string; interface ICallBack {(a: string): string; } let fn: callback = function(a) {}; let fn: ICallBa…...
2024/4/18 14:09:02 - 正则表达式和Python re库的使用
正则是什么 实现字符串的检索、替换、匹配验证 实际操作 import recontent = Hello 123 4567 World_This is a Regex Demo print(len(content)) result = re.match(^Hello\s\d\d\d\s\d{4}\s\w{10},content) print(result)^Hello\s\d\d\d\s\d{4}\s\w{10}^ 表示正则的开始 \s 表示…...
2024/4/16 3:29:09 - 第三课——色彩空间
#色彩空间,1.啥是色彩空间2.常见色彩空间(RGB,HSV,HIS,YCrCB,YUV)3.相互转换 #HSV:H:0-180,S:0-255,V:0-255 import cv2 as cv import numpy as np #色彩空间相互转换 def colocr_space_demo(image): yuv=cv.cvtColor(image,cv.COLOR_BGR2YUV) cv.imshow(“input ima…...
2024/4/20 15:45:30 - STM32入坑教程(二)让你的LED灯闪烁起来
单片机入坑教程(二)让你的LED灯闪烁起来 唠唠两句 上一个博客已经说了点亮一个LED灯的具体方法了,本次来说说如何让你的LED灯闪烁起来。 LED灯无非只有亮和灭两种状态,这两种状态其实我们都已经会了:给它加所需要的电压即亮,撤去电压即灭。这个可以根据实际电路给LED灯高低…...
2024/4/20 8:12:36 - leetcode-141. 环形链表刷题笔记(c++)
写在前面首先思路正确是高效解题的关键 战略性失误,思路肿是错怎么破 ==||题目详情 给定一个链表,判断链表中是否有环。为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。示例 1:输入:he…...
2024/4/16 3:28:59
最新文章
- 按钮(秒懂CSS按钮的使用)
目录 一、按钮介绍 1.概念 2.特点 3.功能 二、按钮用法 1.按钮的使用 2.按钮的样式 3.按钮颜色 4.按钮大小 5.圆角按钮 6.按钮边框颜色 7.按钮鼠标悬停 8.按钮阴影 9.禁用按钮 10.按钮宽度 三、按钮实例 1.交互式按钮 2.扩展动画按钮 3.播放/暂停按钮 四、应用场景…...
2024/4/20 23:35:51 - 梯度消失和梯度爆炸的一些处理方法
在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...
2024/3/20 10:50:27 - 方案分享 | 嵌入式指纹方案
随着智能设备的持续发展,指纹识别技术成为了现在智能终端市场和移动支付市场中占有率最高的生物识别技术。凭借高识别率、短耗时等优势,被广泛地运用在智能门锁、智能手机、智能家居等设备上。 我们推荐的品牌早已在2015年进入指纹识别应用领域ÿ…...
2024/4/16 10:23:30 - ASP.NET Core 标识(Identity)框架系列(一):如何使用 ASP.NET Core 标识(Identity)框架创建用户和角色?
前言 ASP.NET Core 内置的标识(identity)框架,采用的是 RBAC(role-based access control,基于角色的访问控制)策略,是一个用于管理用户身份验证、授权和安全性的框架。 它提供了一套工具和库&…...
2024/4/17 21:43:41 - 【外汇早评】美通胀数据走低,美元调整
原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...
2024/4/19 14:24:02 - 【原油贵金属周评】原油多头拥挤,价格调整
原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...
2024/4/19 18:20:22 - 【外汇周评】靓丽非农不及疲软通胀影响
原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...
2024/4/19 11:57:31 - 【原油贵金属早评】库存继续增加,油价收跌
原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...
2024/4/20 23:26:47 - 【外汇早评】日本央行会议纪要不改日元强势
原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...
2024/4/19 11:57:52 - 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响
原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...
2024/4/19 11:57:53 - 【外汇早评】美欲与伊朗重谈协议
原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...
2024/4/19 11:58:14 - 【原油贵金属早评】波动率飙升,市场情绪动荡
原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...
2024/4/19 11:58:20 - 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试
原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...
2024/4/20 7:40:48 - 【原油贵金属早评】市场情绪继续恶化,黄金上破
原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...
2024/4/19 11:58:39 - 【外汇早评】美伊僵持,风险情绪继续升温
原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...
2024/4/19 11:58:51 - 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势
原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...
2024/4/20 3:12:02 - 氧生福地 玩美北湖(上)——为时光守候两千年
原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...
2024/4/19 11:59:15 - 氧生福地 玩美北湖(中)——永春梯田里的美与鲜
原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...
2024/4/19 11:59:23 - 氧生福地 玩美北湖(下)——奔跑吧骚年!
原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...
2024/4/19 11:59:44 - 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!
原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...
2024/4/19 11:59:48 - 「发现」铁皮石斛仙草之神奇功效用于医用面膜
原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...
2024/4/19 12:00:06 - 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者
原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...
2024/4/20 21:12:02 - 广州械字号面膜生产厂家OEM/ODM4项须知!
原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...
2024/4/19 12:00:25 - 械字号医用眼膜缓解用眼过度到底有无作用?
原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...
2024/4/19 12:00:40 - 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...
解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...
2022/11/19 21:17:18 - 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。
%读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...
2022/11/19 21:17:16 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...
win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...
2022/11/19 21:17:15 - 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...
有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...
2022/11/19 21:17:14 - win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...
置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...
2022/11/19 21:17:13 - 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...
Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...
2022/11/19 21:17:12 - 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...
有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...
2022/11/19 21:17:11 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...
今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...
2022/11/19 21:17:10 - 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...
只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...
2022/11/19 21:17:09 - 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?
原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...
2022/11/19 21:17:08 - 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...
关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...
2022/11/19 21:17:05 - 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...
钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...
2022/11/19 21:17:05 - 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...
前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...
2022/11/19 21:17:04 - 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...
本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...
2022/11/19 21:17:03 - 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...
许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...
2022/11/19 21:17:02 - 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...
配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...
2022/11/19 21:17:01 - 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...
不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...
2022/11/19 21:17:00 - 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...
当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...
2022/11/19 21:16:59 - 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...
我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢࿰…...
2022/11/19 21:16:58 - 如何在iPhone上关闭“请勿打扰”
Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...
2022/11/19 21:16:57