使用 GraalVM 和 Spring Native 在 Apple M1 架构上构建原生镜像

工程 | Josh Long | 2022 年 3 月 23 日 | ...

终于到了!他们做到了!他们在赶在我出门,用我闪亮的新笔记本电脑在路上构建应用程序之前及时做到了!太棒了!!Oracle 和 GraalVM 团队发布了 GraalVM 以及适用于 Apple M1 的 GraalVM 原生镜像功能!我等这一天已经等了,等了,等了好久好久!我早在 2020 年宣布那天就买了第一台 Apple M1(有人还记得那么久远吗?那,从气象学的角度来说,是疫情初期)。

Apple 的 M1 设备是速度惊人、能效极高的强大机器,它们在性能上远超几乎所有最强劲、最新款的 Intel/NVidia 配置,同时消耗的电量却少得可怜。总之,我非常喜欢它们。但转向这种新架构并非没有遇到困难。

一些 Adobe 应用程序(如 Adobe Premiere 和 Adobe After Effects)、Docker 和 GraalVM 是让我的 M1 机器(我已从第一台 13 英寸 MacBook Pro 2020 M1 升级到 16 英寸 MacBook Pro 2021 M1 Max 型号)成为我日常主力机的最大障碍。Docker 已经改进了。Adobe Premiere 已经改进了,After Effects 也差不远了(但仍然没有好消息)。而现在,就在昨天,经过漫长的等待,GraalVM 也改进了。

然而,它还没有正式发布 (GA),甚至还没有包含在出色的 SDKman 项目中。所以安装它可能很麻烦。你们还记得手动将东西添加到 `PATH` 中吗?我记得,而且我不喜欢!所以我 Google 了一下,但一无所获。然后,就在我准备接受不得不工作的命运时,我意识到我那位不可思议的朋友、社区传奇 Matt Raible (@mraible) 已经找到了解决方法。于是我联系了他,他给我发了他在 M1 构建和 Spring Native 上提交的这个工单,详细说明了他如何使其工作以及如何验证突破。

以下是他提供给我的步骤。注意:我认为自从 Matt 写博客以来,他们已经发布了新版本的 GraalVM,所以我的版本号比他的偏离 `.1`。

首先,您需要下载开发者预览版本。我正在使用 Java 17 的 `22.2.0-dev`。所以我选择下载此二进制文件

我将该 `.tgz` 文件放在我的 `$HOME/Desktop` 上,然后运行此脚本来安装它并使其可供 `SDKMan` 使用。这些步骤与 Matt 在其原始工单中详细说明的相同。

#!/usr/bin/env bash 
BUILD=22.2.0
INSTALLED_PATH=$HOME/.sdkman/candidates/java/${BUILD}.dev.r17-grl
cd $HOME/Desktop/  
rm -rf graalvm-ce-java17-${BUILD}-dev  || echo "no directory to delete."
tar zxpf graalvm-ce-java17-darwin-aarch64-dev.tar.gz 
rm -rf $INSTALLED_PATH || echo "no directory to delete."
mv $HOME/Desktop/graalvm-ce-java17-${BUILD}-dev/Contents/Home $INSTALLED_PATH
sudo xattr -r -d com.apple.quarantine ~/.sdkman/candidates/java/${BUILD}.dev.r17-grl

将所有这些指令放入名为 `install.sh` 的文本文件中,放在您的 `$HOME/Desktop` 上,然后运行 `chmod a+x $HOME/Desktop/install.sh` 使其可执行,然后用 `$HOME/Desktop/install.sh` 运行它。

您需要将这个新安装的版本设置为您操作系统的默认版本,所以请发出以下命令

BUILD=22.2.0 
sdk default java ${BUILD}.dev.r17-grlz

完成后,您需要使用 GraalVM 发行版中的 `gu` 工具安装 `native-image` 编译器命令

gu install native-image

打开一个新的 shell 并验证一切正常

➜  java --version 
openjdk 17.0.3 2022-04-19
OpenJDK Runtime Environment GraalVM CE 22.2.0-dev (build 17.0.3+4-jvmci-22.1-b03)
OpenJDK 64-Bit Server VM GraalVM CE 22.2.0-dev (build 17.0.3+4-jvmci-22.1-b03, mixed mode, sharing)

这样,您应该就都设置好了。然后您需要一个应用程序来测试。所以,去我的第二个最喜欢的网上冲浪地点,start dot spring dot io,并生成一个新应用程序预先配置好所有内容。点击 `Generate`,然后解压缩并用您选择的 Java IDE 打开生成的 `pom.xml`。

将 `M1NativeApplication.java` 替换为以下内容

package com.example.m1native;

import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.annotation.Id;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;

import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;

@SpringBootApplication
public class M1nativeApplication {

    public static void main(String[] args) {
        SpringApplication.run(M1nativeApplication.class, args);
    }

    @Bean
    RouterFunction<ServerResponse> routes(CustomerRepository repository) {
        return route()
                .GET("/customers", request -> ok().body(repository.findAll(), Customer.class))
                .build();
    }

    @Bean
    ApplicationRunner runner(CustomerRepository repository) {
        return args -> repository.findAll().subscribe(System.out::println);
    }
}

interface CustomerRepository extends ReactiveCrudRepository<Customer, Integer> {
}

record Customer(@Id Integer id, String name) {
}

添加 `src/main/resources/data.sql`

insert into customer (name ) values('Josh');
insert into customer (name ) values('Mario');
insert into customer (name ) values('Amey');

添加 `src/main/resources/schema.sql`

create table if not exists customer
(
    id   serial primary key,
    name varchar(255) not null
);

好了:您拥有了一个响应式、非阻塞的 Web 应用程序,它正在与嵌入式内存 SQL 数据库通信。让我们将它变成一个原生镜像。在命令行中,运行以下咒语

mvn -Pnative -DskipTests clean package 

这大约需要 45 秒完成。喝几口咖啡,然后运行目标目录中的应用程序

➜ target/m1native
...
2022-03-23 16:24:15.063  INFO 13901 --- [ ... ] Started M1nativeApplication in 0.036 seconds (JVM running for 0.038)
Customer[id=1, name=Josh]
Customer[id=2, name=Mario]
Customer[id=3, name=Amey]

太棒了!GraalVM 万岁!这太惊人了!我知道肯定会有 bug 或问题,但事实是,对于只是一个开发者预览版本,它却能如此好地工作?这真是太酷了。

现在要弄清楚如何处理我那吱吱作响、老旧且不相关的 Intel MacBook Pro 了……

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有