Deploying a Packaged Function

Spring Cloud Function 提供了一个“deployer”库,允许您使用隔离类加载器启动 jar 文件(或 exploded 存档,或 jar 文件集)并公开其中定义的函数。这是一个非常强大的工具,例如,它可以允许您在不更改目标 jar 文件的情况下,将函数适应各种不同的输入输出适配器。无服务器平台通常内置有这种类型的功能,因此您可以将其视为此类平台中函数调用程序的构建块(事实上, Riff Java 函数调用程序使用此库)。

Spring Cloud Function provides a "deployer" library that allows you to launch a jar file (or exploded archive, or set of jar files) with an isolated class loader and expose the functions defined in it. This is quite a powerful tool that would allow you to, for instance, adapt a function to a range of different input-output adapters without changing the target jar file. Serverless platforms often have this kind of feature built in, so you could see it as a building block for a function invoker in such a platform (indeed the Riff Java function invoker uses this library).

标准入口点是向类路径添加 spring-cloud-function-deployer,该部署工具将启动并查找一些配置来告诉它在何处查找函数 jar。

The standard entry point is to add spring-cloud-function-deployer to the classpath, the deployer kicks in and looks for some configuration to tell it where to find the function jar.

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-function-deployer</artifactId>
	<version>${spring.cloud.function.version}</version>
</dependency>

用户至少必须提供一个 spring.cloud.function.location,它是一个包含函数的归档的 URL 或资源位置。它可以选择使用 maven: 前缀通过依赖关系查找来定位工件(有关完整详细信息,请参见 FunctionProperties)。Spring Boot 应用程序使用 MANIFEST.MF 从 jar 文件引导,以找到启动类,因此标准 Spring Boot fat jar 很奏效,例如。如果目标 jar 可以成功启动,则结果是注册在主应用程序的 FunctionCatalog 中的函数。即使函数是在独立类加载器中创建的(默认情况下),也可以通过主应用程序中的代码应用注册的函数。

At a minimum the user has to provide a spring.cloud.function.location which is a URL or resource location for the archive containing the functions. It can optionally use a maven: prefix to locate the artifact via a dependency lookup (see FunctionProperties for complete details). A Spring Boot application is bootstrapped from the jar file, using the MANIFEST.MF to locate a start class, so that a standard Spring Boot fat jar works well, for example. If the target jar can be launched successfully then the result is a function registered in the main application’s FunctionCatalog. The registered function can be applied by code in the main application, even though it was created in an isolated class loader (by deault).

下面是部署包含 “uppercase” 函数并调用它的 JAR 的示例。

Here is the example of deploying a JAR which contains an 'uppercase' function and invoking it .

@SpringBootApplication
public class DeployFunctionDemo {

	public static void main(String[] args) {
		ApplicationContext context = SpringApplication.run(DeployFunctionDemo.class,
				"--spring.cloud.function.location=..../target/uppercase-0.0.1-SNAPSHOT.jar",
				"--spring.cloud.function.definition=uppercase");

		FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
		Function<String, String> function = catalog.lookup("uppercase");
		System.out.println(function.apply("hello"));
	}
}

下面是使用 Maven URI 的示例(取自 FunctionDeployerTests 中的一个测试):

And here is the example using Maven URI (taken from one of the tests in FunctionDeployerTests):

@SpringBootApplication
public class DeployFunctionDemo {

	public static void main(String[] args) {
		String[] args = new String[] {
				"--spring.cloud.function.location=maven://oz.demo:demo-uppercase:0.0.1-SNAPSHOT",
				"--spring.cloud.function.function-class=oz.demo.uppercase.MyFunction" };

		ApplicationContext context = SpringApplication.run(DeployerApplication.class, args);
		FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
		Function<String, String> function = catalog.lookup("myFunction");

		assertThat(function.apply("bob")).isEqualTo("BOB");
	}
}

请记住,使用 MavenProperties 的默认值解析了 maven 资源,如本地和远程存储库、用户、密码等,这些默认值有效地使用了本地默认值并适用于大多数情况。但是,如果您需要自定义,则可以简单地提供类型为 MavenProperties 的 bean,您可以在其中设置其他属性(参见以下示例)。

Keep in mind that Maven resource such as local and remote repositories, user, password and more are resolved using default MavenProperties which effectively use local defaults and will work for majority of cases. However if you need to customize you can simply provide a bean of type MavenProperties where you can set additional properties (see example below).

@Bean
public MavenProperties mavenProperties() {
	MavenProperties properties = new MavenProperties();
	properties.setLocalRepository("target/it/");
	return properties;
}

Supported Packaging Scenarios

目前 Spring Cloud Function 支持几种打包方案,以便在部署函数时提供最大的灵活性。

Currently Spring Cloud Function supports several packaging scenarios to give you the most flexibility when it comes to deploying functions.

Simple JAR

此打包选项意味着不依赖于与 Spring 相关的任何内容。例如;考虑这样的 JAR 包含以下类:

This packaging option implies no dependency on anything related to Spring. For example; Consider that such JAR contains the following class:

package function.example;
. . .
public class UpperCaseFunction implements Function<String, String> {
	@Override
	public String apply(String value) {
		return value.toUpperCase();
	}
}

在部署此类程序包时,您只需指定 locationfunction-class 属性:

All you need to do is specify location and function-class properties when deploying such package:

--spring.cloud.function.location=target/it/simplestjar/target/simplestjar-1.0.0.RELEASE.jar
--spring.cloud.function.function-class=function.example.UpperCaseFunction

在某些情况下,您可能希望将多个函数打包在一起。对于此类方案,您可以使用 spring.cloud.function.function-class 属性,以分号 (;) 为分隔符列出多个类。

It’s conceivable in some cases that you might want to package multiple functions together. For such scenarios you can use spring.cloud.function.function-class property to list several classes delimiting them by ;.

例如,

For example,

--spring.cloud.function.function-class=function.example.UpperCaseFunction;function.example.ReverseFunction

这里我们正在标识两个要部署的函数,现在我们可以通过名称访问函数目录中的函数(例如,catalog.lookup("reverseFunction");)。

Here we are identifying two functions to deploy, which we can now access in function catalog by name (e.g., catalog.lookup("reverseFunction");).

有关更多详细信息,请参阅 here 中提供的完整示例。您还可以在 FunctionDeployerTests 中找到相应的测试。

For more details please reference the complete sample available here. You can also find a corresponding test in FunctionDeployerTests.

  • Component Scanning *

从 3.1.4 版本开始,您可以通过 [Function 组件扫描] 中描述的组件扫描功能简化配置。如果您将功能类放在名为 functions 的程序包中,则可以省略 spring.cloud.function.function-class 属性,因为框架将自动发现功能类并将其加载到函数目录中。在执行函数查找时请记住要遵循的命名约定。例如,函数类 functions.UpperCaseFunction 将在 FunctionCatalog 中以 upperCaseFunction 为名提供。

Since version 3.1.4 you can simplify your configuration thru component scanning feature described in [Function Component Scan]. If you place your functional class in package named functions, you can omit spring.cloud.function.function-class property as framework will auto-discover functional classes loading them in function catalog. Keep in mind the naming convention to follow when doing function lookup. For example function class functions.UpperCaseFunction will be available in FunctionCatalog under the name upperCaseFunction.

Spring Boot JAR

此打包选项意味着存在对 Spring Boot 的依赖,并且 JAR 已生成为 Spring Boot JAR。也就是说,由于已部署 JAR 在独立类加载器中运行,因此与实际部署程序使用的 Spring Boot 版本不会有任何版本冲突。例如;考虑这样的 JAR 包含以下类(可能有一些额外的 Spring 依赖项,提供 Spring/Spring Boot 在类路径上):

This packaging option implies there is a dependency on Spring Boot and that the JAR was generated as Spring Boot JAR. That said, given that the deployed JAR runs in the isolated class loader, there will not be any version conflict with the Spring Boot version used by the actual deployer. For example; Consider that such JAR contains the following class (which could have some additional Spring dependencies providing Spring/Spring Boot is on the classpath):

package function.example;
. . .
public class UpperCaseFunction implements Function<String, String> {
	@Override
	public String apply(String value) {
		return value.toUpperCase();
	}
}

与以前一样,在部署此类程序包时,您只需指定 locationfunction-class 属性:

As before all you need to do is specify location and function-class properties when deploying such package:

--spring.cloud.function.location=target/it/simplestjar/target/simplestjar-1.0.0.RELEASE.jar
--spring.cloud.function.function-class=function.example.UpperCaseFunction

有关更多详细信息,请参阅 here 中提供的完整示例。您还可以在 FunctionDeployerTests 中找到相应的测试。

For more details please reference the complete sample available here. You can also find a corresponding test in FunctionDeployerTests.

Spring Boot Application

此打包选项意味着您的 JAR 是完整的独立 Spring Boot 应用程序,其中函数作为受管的 Spring bean。与以前一样,有一个明显的假设是存在对 Spring Boot 的依赖,并且 JAR 已生成为 Spring Boot JAR。也就是说,由于已部署 JAR 在独立类加载器中运行,因此与实际部署程序使用的 Spring Boot 版本不会有任何版本冲突。例如;考虑这样的 JAR 包含以下类:

This packaging option implies your JAR is complete stand alone Spring Boot application with functions as managed Spring beans. As before there is an obvious assumption that there is a dependency on Spring Boot and that the JAR was generated as Spring Boot JAR. That said, given that the deployed JAR runs in the isolated class loader, there will not be any version conflict with the Spring Boot version used by the actual deployer. For example; Consider that such JAR contains the following class:

package function.example;
. . .
@SpringBootApplication
public class SimpleFunctionAppApplication {

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

	@Bean
	public Function<String, String> uppercase() {
		return value -> value.toUpperCase();
	}
}

鉴于我们实际上正在处理另一个 Spring 应用程序上下文,并且函数是 Spring 管理 bean,除了 location 属性外,我们还指定 definition 属性而不是 function-class

Given that we’re effectively dealing with another Spring Application context and that functions are spring managed beans, in addition to the location property we also specify definition property instead of function-class.

--spring.cloud.function.location=target/it/bootapp/target/bootapp-1.0.0.RELEASE-exec.jar
--spring.cloud.function.definition=uppercase

有关更多详细信息,请参阅 here 中提供的完整示例。您还可以在 FunctionDeployerTests 中找到相应的测试。

For more details please reference the complete sample available here. You can also find a corresponding test in FunctionDeployerTests.

此特定部署选项在其类路径上可能有或没有 Spring Cloud Function。从部署人员的角度来看,这并不重要。

This particular deployment option may or may not have Spring Cloud Function on it’s classpath. From the deployer perspective this doesn’t matter.