Go, Go, GraalVM with Spring Native: 我在原生镜像之城(Native Image-ville)的冒险之旅

工程 | Josh Long | 2021年12月29日 | ...

嗨,Spring 的各位!新年快乐!真不敢相信我们已经走到了这一步,但我们确实做到了。去年的工作异常繁忙,其中我最喜欢的部分就是有机会使用 Spring Native 来构建基于 GraalVM 的、针对特定架构的原生镜像。

我们发布了 Spring Native 0.11,这真是太棒了,因为它包含了一个全新的 AOT(提前编译)引擎,该引擎彻底重写了我们将 Spring Boot 应用程序转换为 GraalVM 原生镜像的方式。在过去的两年里,我一直在大量使用 GraalVM,而这次新版本是 Spring Native 故事中一个巨大、革命性的进步,也是 Spring Framework 6 和 Spring Boot 3(都将在 2022 年发布)旅程中迈出的巨大一步。

在过去的一个月里,我也一直在对新版本进行大量试验。Spring Native 对于 Spring 本身支持的大量用例都工作得很好,因此,对于大多数应用程序来说,我发现无需任何更改即可正常运行。即便如此,有些东西在 Spring Native 环境或任何 GraalVM 环境中都需要一些帮助才能正常工作。例如,你需要告诉 GraalVM 你正在做什么可能令它困惑的事情——代理、序列化、资源加载等。Spring Native 提供了一个机制——提示(hints)——你可以通过它来实现这一点。这很简单。但它仍然需要完成。因此,我一直在一些我认为可能需要帮助的项目中进行尝试,试图让它们正常工作。

MyBatis 与 Spring Native

我让 Spring 和 MyBatis 协同工作,并将其放入了一个示例分支。 更多信息请参阅此博客。让 Spring 和 MyBatis Spring Boot 自动配置工作起来很有挑战性。我开始一点一点地重建自动配置,并设法构建了一个明显不太有用、不太健壮的 适用于 MyBatis 的 Spring Boot 自动配置,它也能很好地与 Spring Native 一起工作。希望我们可以以此为基础,找到如何弥合差距并让提供的、受支持的自动配置也正常工作。我已经与 MyBatis 团队的一些人谈论过可能包含这项工作。 (祈祷!)。有了这个概念验证的 Spring Boot 自动配置和 Spring Native 配置,你可以创建这样的 MyBatis SQL Mapper:


@Mapper
public interface CityMapper {

	@Insert("INSERT INTO city (name, state, country) VALUES(#{name}, #{state}, #{country})")
	@Options(useGeneratedKeys = true, keyProperty = "id")
	void insert(City city);

	@Select("SELECT id, name, state, country FROM city ")
	Collection<City> findAll();
}

Spring Retrosocket 与 Spring Native

我还更新了 Spring Retrosocket 项目,使其能够与 Spring Native 一起工作。Spring Retrosocket 是一个声明式的、类似 FeignRetrofit 的客户端,用于基于 RSocket 的服务。

@RSocketClient
interface GreetingsClient {

	@MessageMapping("hello")
	Mono<String> hello(Mono<String> name);
}

Kubernetes Java 客户端与 Spring Native

接着,我将注意力转向了让 Kubernetes Java 客户端在 Spring Native 和 GraalVM 环境中良好工作。如果你想为 Kubernetes 构建内存高效的控制器和操作员,Kubernetes Java 客户端至关重要。我提到过 GraalVM 原生镜像非常内存高效吗?当然,这取决于你在应用程序中做什么,但我的典型应用程序最终占用 40 到 55 兆字节的 RAM(嗯,准确来说是 RSS)。而且启动时间仅需几十毫秒。官方的 Kubernetes-for-Java 客户端有一个 Spring Boot 自动配置。所以,我所要做的就是编写 简单的 Spring Native 配置,以使此类应用程序在 Spring Native 和 GraalVM 环境中良好工作。 我在这里详细解释了这一点。总而言之,现在不仅可以使用你喜欢的开发框架来构建出色的 Kubernetes 资源和控制器,还可以以低占用空间的方式将其部署到你组织的集群中。

Fabric8 与 Spring Native

说到 Kubernetes 客户端,我还让 RedHat 的精彩的 Fabric8.io Kubernetes 客户端Spring Native 一起工作。我找到一个很棒的示例操作员和自定义资源定义,然后使用我的 Fabric8 Spring Native hints 让它正常工作。这是一个更全面的示例,并且效果非常好。这是基于 Rohan Kanojia 的一个精彩示例,我对其进行了修改,以使用 Spring Boot 和 Spring Native。

这种方法很有前景!结合 Spring Native、官方 Kubernetes Java 客户端和 Fabric8 客户端,你完全有理由使用 Spring Boot 来构建下一个 Kubernetes 操作员。

Spring GraphQL 与 Spring Native

然后,我将注意力转向了 Spring GraphQL 和 Spring Native。只要你覆盖 `GraphQlSourceBuilder` 如何推导出用于向引擎提供 GraphQL 端点模式的 Spring Framework `Resource` 实例,Spring GraphQL 就能很好地工作。这不像它可能的那样容易,但仍然只需要额外的一两个 `@Bean` 或几行代码就能让它工作。很好。麻烦在于当你使用 Spring GraphQL 并想查询 Spring GraphQL 模式本身的元模型时。拥有 GraphQL 元模型在例如使用 `/graphiql/` 交互式控制台查询数据时非常方便。确实花了一些功夫,但我做到了。我在 这篇博文中进一步解释了这一点

有了这个,我就可以像这样部署一个 GraphQL 控制器:


@Controller
class CustomerGraphQlController {

	private final CustomerRepository repository;
 	
 	CustomerGraphQlController(CustomerRepository repository) {
 		this.repository = repository ;
 	} 

	@QueryMapping
	Flux<Customer> customers() {
		return this.repository.findAll();
	}
}

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

然后使用以下模式:

type Query {
    customers : [Customer]
}
type Customer {
    id: ID
    name :String
}

然后打开示例,在 `https://:8080/graphiql/` 上,发出以下查询:

query {
 customers { id, name }
}

并获得我期望的结果!

杂项

我还处理了许多其他我想让 Spring Native 支持的事情。所以,这是 CommonMark(Java 中的 Markdown 解析器)的 Spring Native 配置:CommonMark,一个 Java 中的 Markdown 解析器

以下是我为让 Apache Lucene 在 Spring Native 项目中工作而必须添加的各种类。当然,这个示例更复杂,并且使用了 GraalVM 替换(substitutions)和典型的 Spring Native 配置。但它确实有效,而且效果很好!

哦,我是否提到了我和 Ronald Dehuysser 一起让 Jobrunr(一个分布式作业调度引擎)在 GraalVM 环境中与 Spring Native 一起工作?因为我做到了,结果非常棒

所有这些都是在最近几周完成的:可能性是无穷无尽的,我迫不及待地想看到越来越多的 Spring 生态系统中涌现出 GraalVM 集成。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

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

了解更多

获得支持

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

了解更多

即将举行的活动

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

查看所有