冲啊,冲啊,GraalVM 与 Spring Native:我在 Native Image 世界里的冒险

工程技术 | 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 自动配置协同工作是一项挑战。我开始一点一点地重建自动配置,并设法构建了一个毫无疑问不太有用、不够健壮的 与 Spring Native 也能很好地协同工作的 MyBatis Spring Boot 自动配置。希望我们能以此为基础,找出如何弥合差距,并使提供的、受支持的自动配置也能工作。我已经在与 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 是一个用于基于 RSocket 的服务的声明式客户端,类似于 FeignRetrofit

@RSocketClient
interface GreetingsClient {

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

Kubernetes Java Client 和 Spring Native

然后,我将注意力转向如何让 Kubernetes Java 客户端在 Spring Native 和 GraalVM 环境中良好工作。如果你想为 Kubernetes 构建内存高效的控制器和操作符,Kubernetes Java 客户端是必不可少的。我有没有提到 GraalVM 原生镜像的内存效率 非常 高?当然,这取决于你在应用中做什么,但我典型的应用最终会占用 40 到 55 兆字节的 RAM(更准确地说,是 RSS)。此外,启动时间只需几十毫秒。官方的 Kubernetes for Java 客户端带有 Spring Boot 自动配置。所以我所要做的就是编写使这些应用在 Spring Native 和 GraalVM 环境中良好工作所需的 简单的 Spring Native 配置我在此详细解释。总而言之,现在不仅可以使用你喜欢的开发框架构建出色的 Kubernetes 资源和控制器,还可以以低资源占用方式将它们部署到你的组织集群中。

Fabric8 和 Spring Native

说到 Kubernetes 客户端,我也让 RedHat 出色的 Fabric8.io Kubernetes 客户端Spring Native 协同工作了。我找到了一个非常棒的示例操作符和自定义资源定义,然后使用我的 Fabric8 Spring Native 提示 让它工作起来。这是一个功能更全面的例子,并且效果极佳。这是基于 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
}

然后打开示例 http://localhost:8080/graphiql/,并执行以下查询

query {
 customers { id, name }
}

并得到了我预期的结果!

杂项

我还处理了许多其他想要与 Spring Native 协同工作的事情。所以,这里是 CommonMark(一个用 Java 编写的 Markdown 解析器)的 Spring Native 配置

这里是我为了让 Apache Lucene 在 Spring Native 项目中工作而不得不添加的各种类。当然,这个例子更复杂,使用了 GraalVM 替换 和典型的 Spring Native 配置。但它奏效了,而且效果很好!

哦,我有没有提到我曾与 Ronald Dehuysser 合作,让 Jobrunr(一个分布式作业调度引擎)在 GraalVM 环境中与 Spring Native 协同工作?我确实做到了,而且结果 棒极了

所有这些工作都是在过去几周内完成的:可能性是无限的,我迫不及待地想看到 Spring 广阔的世界中涌现出越来越多的 GraalVM 集成。

订阅 Spring 新闻快讯

通过 Spring 新闻快讯保持联系

订阅

领先一步

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

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举办的活动

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

查看全部