缘起
FaaS - 无服务器计算,亦即函数即服务,英文 Fuction as a Service,其目标是希望应用不用一直运行着,只有当有请求来的时候,才快速启动这个应用,然后请求一走就停掉这个应用。换句话说,不让应用在背景程式持续的启动着,而是有需要的时候才开启。
这就要求应用要有快速启动,快速停止的能力。
相对庞大的 Jvm 启动时间,加上巨型的 Spring Framework, 很多应用启动动辄分钟级,实现 FaaS 实在是痴心妄想。相比之下,PHP还真是最好的语言,因为 PHP的启动模型本质上就是 FaaS, 你哪哈... 别说了。
话说 Spring 当年就是为了简化 J2EE 的庞杂而出生的,如今也变成了自己讨厌的样子...
软件的问题由软件来解决, Any problem in computer science can be solved with another layer of indirection. 没有什么问题是口罩和护垫解决不了的,如果是,就再加一层。
GraalVM 就是新出现的那一层。
从上面看,这一层增加了对多语言的支持,Truffle Framework 支持Ruby, R, Js编译到JVM,再嫁接一下LLVM,那传统的C/C++等也可以统统编译了。话说 C/C++本来就是直接编译成原生的,这回 C -> LLVM -> GrallVM -> 原生, 你品,你细品,究竟折腾个啥。
所以说加层唯一解决不了的问题是层过多的问题:...except for the problem of too many layers of indirection - Kevlin Henney.
从下面看,GraalVM 可以将 .class 编译成原生代码,去掉了包中不使用的代码,大大提高了启动速度,运行速度据称也大大提高。
人生总是不要脸的轮回。当年微软用Visual J++把 Java 编成原生,速度超快,引来的是与Sun的官司战。Sun被Oracle收购,Oracle搞来搞去又走上了这条螺旋下降的道路。
这样,替换VM直接代来了效率提升,带来了和加内存升级CPU一样的效果。
更多原理上的内容可以参考这篇, 写得很详细。只是少分部内容有点过时了。
实操
下载GraalVM
完整地用类用例可以看看这里,有很多:git clone https://github.com/quarkusio/quarkus-quickstarts.git
Quarkus 框架结合 GraalVM 的各类应用。
下面我们只看几个最简单的 GraalVM 用例.// HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}$ native-image HelloWorld
[helloworld:29104] classlist: 878.53 ms, 0.96 GB
[helloworld:29104] (cap): 5,001.41 ms, 0.96 GB
[helloworld:29104] setup: 7,970.43 ms, 0.96 GB
[helloworld:29104] (clinit): 136.83 ms, 1.20 GB
[helloworld:29104] (typeflow): 3,437.24 ms, 1.20 GB
[helloworld:29104] (objects): 3,531.34 ms, 1.20 GB
[helloworld:29104] (features): 194.35 ms, 1.20 GB
[helloworld:29104] analysis: 7,426.70 ms, 1.20 GB
[helloworld:29104] universe: 243.17 ms, 1.20 GB
[helloworld:29104] (parse): 732.66 ms, 1.67 GB
[helloworld:29104] (inline): 1,048.13 ms, 1.67 GB
[helloworld:29104] (compile): 4,809.83 ms, 2.29 GB
[helloworld:29104] compile: 7,051.94 ms, 2.29 GB
[helloworld:29104] image: 986.33 ms, 2.29 GB
[helloworld:29104] write: 252.78 ms, 2.29 GB
[helloworld:29104] [total]: 24,931.40 ms, 2.29 GB
编译一个helloworld为原生代码需要 24 秒, 同时也需要大量的物理内存,最后一列是内存的使用。
运行,原生:$ time ./helloworld
Hello, World!
real 0m0.010s
user 0m0.004s
sys 0m0.003s
比较传统方式:$ time ./helloworld
Hello, World!
real 0m0.010s
user 0m0.004s
sys 0m0.003s
这个原生的 helloworld 个头也是惊人的:$ ll
total 15712
-rw-r--r-- 1 427 12 8 00:18 HelloWorld.class
-rw-r--r-- 1 116 12 8 00:18 HelloWorld.java
-rwxr-xr-x 1 8032816 12 8 00:19 helloworld
足足 8 Mb.