Spring Cloud 功能_应用程序

Spring Cloud Function是一个具有以下高级目标的项目:

  • 通过功能促进业务逻辑的实现。
  • 将业务逻辑的开发生命周期与任何特定的运行时目标分离,以便相同的代码可以作为 Web 终结点、流处理器或任务运行。
  • 支持跨无服务器提供程序的统一编程模型,以及独立运行(本地或在 PaaS 中)的能力。
  • 在无服务器提供程序上启用 Spring 引导功能(自动配置、依赖关系注入、指标)。

它抽象出所有的运输细节和 基础设施,允许开发人员保留所有熟悉的工具 和流程,并坚定地专注于业务逻辑。

这是一个完整的、可执行的、可测试的 Spring Boot 应用程序。 (实现简单的字符串操作):

@SpringBootApplication
public class Application {

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

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

它只是一个Spring Boot应用程序,因此可以构建,运行和 在本地和CI构建中进行测试,与任何其他Spring相同 启动应用程序。Theis fromandis aReactive StreamsfromProject Reactor.该功能可以是 通过 HTTP 或消息传递访问。​​Function​​​​java.util​​​​Flux​​​​Publisher​

春云功能有4个主要特点:

简而言之,春云功能提供以下功能: 1.包装器的类型,并将它们作为HTTP暴露给外界 端点和/或消息流侦听器/发布者与 RabbitMQ、Kafka 等。​​@Beans​​​​Function​​​​Consumer​​​​Supplier​

  • 编程风格的选择 - 响应式、命令式或混合式。
  • 函数组合和适应(例如,用反应式组合命令式函数)。
  • 支持具有多个输入和输出的反应式功能,允许由函数处理合并、加入和其他复杂的流操作。
  • 输入和输出的透明类型转换。
  • 特定于目标平台的部署打包函数(例如,Project Riff、AWS Lambda 等)
  • 适配器,用于将功能作为 HTTP 端点等向外界公开。
  • 使用隔离的类装入器部署包含此类应用程序上下文的 JAR 文件,以便可以将它们打包到单个 JVM 中。
  • 适用于AWS Lambda​,​Azure​,​Google Cloud Functions​,​Apache OpenWhisk​以及其他可能的“无服务器”服务提供商的适配器。

Spring Cloud 是在非限制性 Apache 2.0 许可证下发布的。如果您想为文档的这一部分做出贡献,或者如果您发现错误,请在github 的项目中找到源代码和问题跟踪器。

开始

从命令行构建(并“安装”示例):

$ ./mvnw clean install

(如果你喜欢 YOLO 添加。​​-DskipTests​

运行其中一个样本,例如

$ java -jar spring-cloud-function-samples/function-sample/target/*.jar

这将运行应用程序并通过 HTTP 公开其功能,因此您可以 将字符串转换为大写,如下所示:

$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d Hello
HELLO

您可以通过分隔多个字符串来转换多个字符串 (a) 使用新线路​​Flux<String>​

$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d 'Hello
> World'
HELLOWORLD

(您可以在终端中使用在文字中插入新行 像这样的字符串。​QJ​

编程模型

函数目录和灵活的函数签名

Spring Cloud 函数的主要功能之一是适应和支持用户定义函数的一系列类型签名, 同时提供一致的执行模型。 这就是为什么所有用户定义的函数都被转换为规范表示的原因。​​FunctionCatalog​

虽然用户通常根本不需要关心,但知道什么是有用的 用户代码中支持函数类型。​​FunctionCatalog​

同样重要的是要了解Spring Cloud Function为响应式API提供了一流的支持 由Project Actor提供,允许将反应式原语(如andto)用作用户定义函数中的类型,在选择编程模型时提供更大的灵活性 您的函数实现。 响应式编程模型还支持对原本难以实现或不可能实现的功能 使用命令式编程风格。有关此内容的更多信息,请阅读函数Arity部分。​​Mono​​​​Flux​

Java 8 函数支持

Spring Cloud Function包含并构建在Java定义的3个核心功能接口之上。 并从 Java 8 开始可供我们使用。

  • 供应商<O>
  • 功能<I, O>
  • 消费者<I>

简而言之,应用程序上下文中任何类型为或可以注册的 bean。 这意味着它可以从本参考手册中描述的所有功能中受益。​​Supplier​​​​Function​​​​Consumer​​​​FunctionCatalog​

筛选不符合条件的函数

典型的应用程序上下文可能包括有效的 Java 函数,但不打算注册的候选对象。 这些 bean 可以是来自其他项目的自动配置,也可以是任何其他有资格成为 Java 函数的 bean。 该框架提供已知 Bean 的默认过滤,这些 Bean 不应作为向函数目录注册的候选项。 您还可以通过提供 bean 定义名称的逗号分隔列表来向此列表添加其他 bean,使用属性​​FunctionCatalog​​​​spring.cloud.function.ineligible-definitions​

例如

spring.cloud.function.ineligible-definitions=foo,bar

供应商

供应商可以是被动的,也可以是命令式的。从调用的角度来看,这应该没有区别 向该供应商的实施者披露。但是,在框架内使用时 (例如,弹簧云流),供应商,特别是反应性, 通常用于表示流的源,因此它们被调用一次以获取流(例如,Flux) 消费者可以订阅的内容。换句话说,这样的供应商相当于无限流。 然而,相同的反应式供应商也可以表示有限流(例如,轮询的JDBC数据上的结果集)。 在这些情况下,这种反应性供应商必须连接到底层框架的某种轮询机制。​​Supplier<Flux<T>>​​​​Supplier<T>​

为了协助该 Spring 云函数提供了一个标记注释,以表明此类供应商产生 有限流,可能需要再次轮询。也就是说,重要的是要了解Spring Cloud功能本身 不为此批注提供任何行为。​​org.springframework.cloud.function.context.PollableSupplier​

此外,注释公开一个可拆分属性,以发出流的信号 需要拆分(请参阅拆分器弹性公网IP)​​PollableSupplier​​)

下面是示例:

@PollableSupplier(splittable = true)
public Supplier<Flux<String>> someSupplier() {
return () -> {
String v1 = String.valueOf(System.nanoTime());
String v2 = String.valueOf(System.nanoTime());
String v3 = String.valueOf(System.nanoTime());
return Flux.just(v1, v2, v3);
};
}

功能

函数也可以以命令式或响应式方式编写,但与供应商和消费者不同的是,有 对于实现者来说,除了理解在框架中使用时,没有特殊的考虑因素 如春云流等,反应功能是 仅调用一次以传递对流(Flux 或 Mono)的引用,并且每个事件调用一次命令式。

消费者

消费者有点特殊,因为它有返回类型, 这意味着阻止,至少是潜在的。很可能你不会 需要写,但如果你确实需要这样做, 请记住订阅输入通量。​​void​​​​Consumer<Flux<?>>​

功能组成

函数组合是一种功能,允许将多个函数组合成一个。 核心支持基于Function.andThen(..) 提供的函数组合功能自 Java 8 起提供支持。但是,最重要的是,我们提供了一些其他功能。

声明性函数组合

此功能允许您使用(管道)或(逗号)分隔符以声明性方式提供组合指令 提供财产时。​​|​​​​,​​​​spring.cloud.function.definition​

例如

--spring.cloud.function.definition=uppercase|reverse

在这里,我们有效地提供了单个函数的定义,该函数本身是 函数和函数。事实上,这就是为什么属性名称是定义而不是名称的原因之一, 因为函数的定义可以是几个命名函数的组合。 如前所述,您可以使用代替管道(例如)。​​uppercase​​​​reverse​​​​,​​​​…definition=uppercase,reverse​

组合非函数

Spring Cloud Function还支持将供应商与以及与组成。 这里重要的是了解这些定义的最终产品。 将供应商与功能组合仍会产生供应商,而将供应商与消费者组成将有效地呈现可运行。 遵循相同的逻辑,将函数与消费者组成将产生消费者。​​Consumer​​​​Function​​​​Function​​​​Consumer​

当然,您不能组合不可组合的内容,例如消费者和功能,消费者和供应商等。

函数路由和过滤

从版本2.2开始,弹簧云功能提供了路由功能,允许 您调用单个函数,该函数充当您希望调用的实际函数的路由器 此功能在某些维护配置的 FAAS 环境中非常有用 对于多个函数可能很麻烦,或者公开多个函数是不可能的。

Theis在FunctionCatalog中以该名称注册。为简单起见 和一致性,你也可以参考常量。​​RoutingFunction​​​​functionRouter​​​​RoutingFunction.FUNCTION_NAME​

此函数具有以下签名:

public class RoutingFunction implements Function<Object, Object> {
. . .
}

路由指令可以通过多种方式进行通信。我们支持通过消息标头、系统提供说明 属性以及可插拔策略。所以让我们看看一些细节

消息路由回调

这是一种策略,用于帮助确定路由到函数定义的名称。​​MessageRoutingCallback​

public interface MessageRoutingCallback {
FunctionRoutingResult routingResult(Message<?> message);
. . .
}

您需要做的就是实现并将其注册为要拾取的 bean。 例如:​​RoutingFunction​

@Bean
public MessageRoutingCallback customRouter() {
return new MessageRoutingCallback() {
@Override
FunctionRoutingResult routingResult(Message<?> message) {
return new FunctionRoutingResult((String) message.getHeaders().get("func_name"));
}
};
}

在前面的示例中,您可以看到一个非常简单的实现,它确定来自传入消息的 Message 标头的函数定义,并返回包含要调用的函数定义的实例。​​MessageRoutingCallback​​​​func_name​​​​FunctionRoutingResult​

此外,还提供了另一个构造函数,允许您提供作为第二个参数的实例,以便在下游使用。 这主要用于运行时优化。为了更好地理解这种情况,让我们看一下以下场景。 您需要根据付款负载类型进行路由。但是,输入消息通常以 JSON 有效负载 (as) 的形式出现。挨次 若要确定路由到函数定义,需要首先处理此类 JSON,并可能创建目标类型的实例。 完成该确定后,您可以将其传递给它仍然具有对具有未处理有效负载的原始消息的引用 这意味着在下游的某个地方,需要重复类型转换/转换。​​FunctionRoutingResult​​​​Message​​​​byte[]​​​​RoutingFunction​

允许您创建一个新的转换后的有效负载作为将指示使用此类下游的一部分。因此,您有效地让框架从您已经完成的工作中受益。​​Message​​​​FunctionRoutingResult​​​​RoutingFunction​​​​Message​

邮件头

如果输入参数的类型,则可以通过设置一个 oforMessage 标头来传达路由指令。 正如该属性的名称所暗示的那样,依赖于 Spring 表达式语言 (SpEL)。 对于更多的静态情况,您可以使用标头,它允许您提供 单个函数的名称(例如,)或组合指令(例如,)。 对于更动态的情况,您可以使用标头并提供应解析的 SpEL 表达式 到函数的定义中(如上所述)。​​Message<?>​​​​spring.cloud.function.definition​​​​spring.cloud.function.routing-expression​​​​spring.cloud.function.routing-expression​​​​spring.cloud.function.definition​​​​…definition=foo​​​​…definition=foo|bar|baz​​​​spring.cloud.function.routing-expression​

SpEL 评估上下文的根对象是 实际输入参数,因此在 您可以构造具有访问权限的表达式 到两者(例如,)。​​Message<?>​​​​payload​​​​headers​​​​spring.cloud.function.routing-expression=headers.function_name​

SpEL允许用户提供要执行的Java代码的字符串表示。鉴于可以通过消息标头提供,这意味着设置此类表达式的能力可能会暴露给最终用户(即使用Web模块时的HTTP标头),这可能会导致一些问题(例如,恶意代码)。为了管理这一点,所有通过消息标头的表达式将仅根据其功能有限且旨在仅评估上下文对象(在我们的例子中为 Message)进行计算。另一方面,通过属性或系统变量设置的所有表达式都会被计算,这允许Java语言的完全灵活性。 虽然通过系统/应用程序属性或环境变量设置表达式通常被认为是安全的,因为它在正常情况下不会向最终用户公开,但在某些情况下,可见性以及更新系统、应用程序和环境变量的能力确实通过某些 Spring 项目或第三方提供的 Spring 引导执行器端点或最终用户的自定义实现暴露给最终用户。必须使用行业标准 Web 安全实践来保护此类端点。 Spring Cloud 函数不会公开任何此类端点。​​spring.cloud.function.routing-expression​​​​SimpleEvaluationContext​​​​StandardEvaluationContext​

在特定执行环境/模型中,适配器负责转换和通信和/或通过消息标头。 例如,当使用spring-cloud-function-web时,你可以提供HTTP。 标头和框架将传播它以及其他 HTTP 标头作为消息标头。​​spring.cloud.function.definition​​​​spring.cloud.function.routing-expression​​​​spring.cloud.function.definition​

应用程序属性

路由指令也可以通过应用程序属性进行通信。中描述的规则 上一节也适用于此处。唯一的区别是您将这些说明作为 应用程序属性(例如,)。​​spring.cloud.function.definition​​​​spring.cloud.function.routing-expression​​​​--spring.cloud.function.definition=foo​

重要的是要了解 providingoras 消息标头仅适用于命令性函数(例如,)。 也就是说,我们只能使用命令式函数路由每条消息。使用反应式函数,我们无法路由每条消息。因此,只能将路由指令作为应用程序属性提供。 这一切都与工作单元有关。在命令式函数中,工作单元是消息,因此我们可以基于这种工作单元进行路由。 对于反应函数,工作单元是整个流,因此我们仅根据应用程序提供的指令进行操作 属性并路由整个流。​​spring.cloud.function.definition​​​​spring.cloud.function.routing-expression​​​​Function<Foo, Bar>​

路由指令的优先级顺序

鉴于我们有几种提供路由指令的机制,了解 同时使用多种机制时的冲突解决,因此顺序如下:

  1. ​MessageRoutingCallback​​(如果函数是命令式的,则无论是否定义了任何其他内容,都将接管)
  2. 消息头(如果函数是命令式且未提供)MessageRoutingCallback
  3. 应用程序属性(任何函数)

函数过滤

过滤是只有两条路径的路由类型 - “去”或“丢弃”。就功能而言,这意味着 你只想在某个条件返回'true'时调用某个函数,否则你想丢弃输入。 但是,当涉及到丢弃输入时,对于它在应用程序上下文中的含义有多种解释。 例如,您可能希望记录它,或者您可能希望维护丢弃消息的计数器。您可能还想什么都不做。 由于这些不同的路径,我们不提供如何处理丢弃消息的常规配置选项。 相反,我们只是建议定义一个简单的消费者,它将表示“丢弃”路径:

@Bean
public Consumer<?> devNull() {
// log, count or whatever
}

现在,您可以让实际上只有两条路径的路由表达式有效地成为过滤器。例如:

--spring.cloud.function.routing-expression=headers.contentType.toString().equals('text/plain') ? 'echo' : 'devNull'

每条不符合“回显”功能条件的消息都将转到“devNull”,您可以在其中简单地对它执行任何操作。 签名还将确保不会尝试任何类型转换,从而几乎不会产生执行开销。​​Consumer<?>​

处理反应性输入(例如,发布服务器)时,路由指令必须仅通过函数属性提供。这是 由于反应函数的性质,响应函数只调用一次以传递发布者,其余的 由反应器处理,因此我们无法访问和/或依赖通过个人传达的路由指令 值(例如,消息)。

多个路由器

默认情况下,框架将始终配置单个路由函数,如前面部分所述。但是,有时您可能需要多个路由功能。 在这种情况下,除了现有的实例之外,您还可以创建自己的 bean 实例,只要您为其指定一个名称。​​RoutingFunction​​​​functionRouter​

您可以将 RoutinFunction 作为映射中的键/值对传递。​​spring.cloud.function.routing-expression​​​​spring.cloud.function.definition​

这是一个简单的例子

@Configuration
protected static class MultipleRouterConfiguration {

@Bean
RoutingFunction mySpecialRouter(FunctionCatalog functionCatalog, BeanFactory beanFactory, @Nullable MessageRoutingCallback routingCallback) {
Map<String, String> propertiesMap = new HashMap<>();
propertiesMap.put(FunctionProperties.PREFIX + ".routing-expression", "'reverse'");
return new RoutingFunction(functionCatalog, propertiesMap, new BeanFactoryResolver(beanFactory), routingCallback);
}

@Bean
public Function<String, String> reverse() {
return v -> new StringBuilder(v).reverse().toString();
}

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

以及演示其工作原理的测试

@Test
public void testMultipleRouters() {
System.setProperty(FunctionProperties.PREFIX + ".routing-expression", "'uppercase'");
FunctionCatalog functionCatalog = this.configureCatalog(MultipleRouterConfiguration.class);
Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME);
assertThat(function).isNotNull();
Message<String> message = MessageBuilder.withPayload("hello").build();
assertThat(function.apply(message)).isEqualTo("HELLO");

function = functionCatalog.lookup("mySpecialRouter");
assertThat(function).isNotNull();
message = MessageBuilder.withPayload("hello").build();
assertThat(function.apply(message)).isEqualTo("olleh");
}

输入/输出丰富

很多时候,您需要修改或优化传入或传出消息,并保持代码中没有功能性问题。您不想在业务逻辑中执行此操作。

您始终可以通过函数组合来完成它。这种方法有几个好处:

  • 它允许您将此非功能性关注点隔离到一个单独的函数中,您可以将该函数与业务函数组合为函数定义。
  • 它为您提供了在传入消息到达实际业务功能之前可以修改的内容的完全自由(和危险)。
@Bean
public Function<Message<?>, Message<?>> enrich() {
return message -> MessageBuilder.fromMessage(message).setHeader("foo", "bar").build();
}

@Bean
public Function<Message<?>, Message<?>> myBusinessFunction() {
// do whatever
}

然后通过提供以下函数定义来组合函数。​​enrich|myBusinessFunction​

虽然所描述的方法是最灵活的,但它也是最复杂的,因为它需要你编写一些代码,使其成为 bean 或 手动将其注册为函数,然后才能将其与业务函数组合在一起,如前面的示例所示。

但是,如果您尝试进行的修改(扩充)像前面示例中一样微不足道,该怎么办?有没有更简单、更动态和可配置的 机制来完成相同的任务?

从版本 3.1.3 开始,该框架允许您提供 SpEL 表达式来丰富进入函数的输入和 并从中输出。让我们以其中一个测试为例。

@Test
public void testMixedInputOutputHeaderMapping() throws Exception {
try (ConfigurableApplicationContext context = new SpringApplicationBuilder(
SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run(
"--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.main.lazy-initialization=true",
"--spring.cloud.function.configuration.split.output-header-mapping-expression.keyOut1='hello1'",
"--spring.cloud.function.configuration.split.output-header-mapping-expression.keyOut2=headers.contentType",
"--spring.cloud.function.configuration.split.input-header-mapping-expression.key1=headers.path.split('/')[0]",
"--spring.cloud.function.configuration.split.input-header-mapping-expression.key2=headers.path.split('/')[1]",
"--spring.cloud.function.configuration.split.input-header-mapping-expression.key3=headers.path")) {

FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class);
FunctionInvocationWrapper function = functionCatalog.lookup("split");
Message<byte[]> result = (Message<byte[]>) function.apply(MessageBuilder.withPayload("helo")
.setHeader(MessageHeaders.CONTENT_TYPE, "application/json")
.setHeader("path", "foo/bar/baz").build());
assertThat(result.getHeaders().containsKey("keyOut1")).isTrue();
assertThat(result.getHeaders().get("keyOut1")).isEqualTo("hello1");
assertThat(result.getHeaders().containsKey("keyOut2")).isTrue();
assertThat(result.getHeaders().get("keyOut2")).isEqualTo("application/json");
}
}

在这里,您会看到一个属性,该属性以函数名称(即,)开头,后跟要设置的消息标头键的名称以及作为 SpEL 表达式的值。第一个表达式(对于“keyOut1”)是括在单引号中的文字 SpEL 表达式,有效地将“keyOut1”设置为值。设置为现有“内容类型”标头的值。​​input-header-mapping-expression​​​​output-header-mapping-expression​​​​split​​​​hello1​​​​keyOut2​

您还可以在输入标头映射中观察到一些有趣的功能,我们实际上拆分了现有标头“path”的值,根据索引将 key1 和 key2 的各个值设置为拆分元素的值。

如果由于某种原因提供的表达式计算失败,函数的执行将继续进行,就好像什么都没有发生一样。 但是,您将在日志中看到警告消息,通知您有关它的信息

o.s.c.f.context.catalog.InputEnricher    : Failed while evaluating expression "hello1"  on incoming message. . .

如果您正在处理具有多个输入的函数(下一节),则可以在之后立即使用 index。​​input-header-mapping-expression​

--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key1=‘hello1'
--spring.cloud.function.configuration.echo.input-header-mapping-expression[1].key2='hello2'

功能强度

有时需要对数据流进行分类和组织。例如 考虑一个处理无组织数据的经典大数据用例,假设: “订单”和“发票”,您希望每个都进入单独的数据存储。 这就是函数arity(具有多个输入和输出的函数)支持的地方 来玩。

让我们看一下这样一个函数的示例(完整的实现细节可在此处获得),

@Bean
public Function<Flux<Integer>, Tuple2<Flux<String>, Flux<String>>> organise() {
return flux -> ...;
}

鉴于 Project Reactor 是 SCF 的核心依赖项,我们使用的是它的元组库。 元组通过向我们传达基类型信息,为我们提供了独特的优势。 在 SCSt 的上下文中,两者都非常重要。 基数让我们知道 需要创建多少个输入和输出绑定并绑定到相应的 函数的输入和输出。了解类型信息可确保正确的类型 转换。

此外,这是用于绑定的命名约定的“索引”部分 名称开始发挥作用,因为在此函数中,两个输出绑定 名字是和。​​organise-out-0​​​​organise-out-1​

重要提示:目前,函数 arity支持响应式函数 () 以复杂事件处理为中心 其中对事件汇合的评估和计算通常需要查看 事件流而不是单个事件。​​Function<TupleN<Flux<?>…>, TupleN<Flux<?>…>>​

输入标头传播

在典型的场景中,输入消息标头不会传播到输出,这是理所当然的,因为函数的输出可能是需要它自己的一组消息标头的其他内容的输入。 但是,有时可能需要这种传播,因此 Spring Cloud Function 提供了几种机制来实现此目的。

首先,您始终可以手动复制标头。例如,如果你有一个带有 take sand return 的签名的函数(即),你可以自己简单而有选择地复制标头。请记住,如果您的函数返回 Message,则框架不会对其执行任何操作,然后正确转换其有效负载。 但是,这种方法可能会有点乏味,尤其是在您只想复制所有标头的情况下。 为了帮助处理此类情况,我们提供了一个简单的属性,允许您在希望传播输入标头的函数上设置布尔标志。 该物业是。​​Message​​​​Message​​​​Function<Message, Message>​​​​copy-input-headers​

例如,假设您具有以下配置:

@EnableAutoConfiguration
@Configuration
protected static class InputHeaderPropagationConfiguration {

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

如您所知,您仍然可以通过向其发送消息来调用此函数(框架将负责类型转换和有效负载提取)

通过简单地设置为,以下断言也将是正确的​​spring.cloud.function.configuration.uppercase.copy-input-headers​​​​true​

Function<Message<String>, Message<byte[]>> uppercase = catalog.lookup("uppercase", "application/json");
Message<byte[]> result = uppercase.apply(MessageBuilder.withPayload("bob").setHeader("foo", "bar").build());
assertThat(result.getHeaders()).containsKey("foo");

类型转换(内容类型协商)

内容类型协商是Spring Cloud Function的核心功能之一,因为它不仅允许将传入数据转换为声明的类型 通过函数签名,但在函数组合期间执行相同的转换,使原本不可组合的(按类型)函数可组合。

为了更好地理解内容类型协商背后的机制和必要性,我们来看看一个非常简单的用例: 以以下函数为例:

@Bean
public Function<Person, String> personFunction {..}

前面示例中所示的函数需要 aobject 作为参数,并生成 String 类型作为输出。如果这样的功能是 用类型调用,比一切正常。但通常函数扮演处理程序的角色,传入数据最常出现 在原始格式中,例如等。为了使框架成功地将传入的数据作为参数传递给 这个函数,它必须以某种方式将传入的数据转换为 atype。​​Person​​​​Person​​​​byte[]​​​​JSON String​​​​Person​

Spring Cloud Function 依赖于 Spring 的两个原生机制来实现这一点。

  1. 消息转换器 - 从传入的消息数据转换为函数声明的类型。
  2. 转换服务 - 从传入的非消息数据转换为函数声明的类型。

这意味着根据原始数据的类型(消息或非消息),Spring Cloud Function将应用一种或另一种机制。

在大多数情况下,当处理作为其他请求的一部分(例如,HTTP,消息传递等)调用的函数时,框架所依赖的, 因为此类请求已经转换为 Spring。换句话说,框架定位并应用适当的。 为此,框架需要用户的一些指令。函数的签名已经提供了这些指令之一 本身(人员类型)。因此,从理论上讲,这应该(在某些情况下,是)足够的。但是,对于大多数用例,为了 选择适当的,框架需要一条额外的信息。那块缺失的就是标题。​​MessageConverters​​​​Message​​​​MessageConverter​​​​MessageConverter​​​​contentType​

此类标头通常作为消息的一部分出现,其中由最初创建此类消息的相应适配器注入。 例如,HTTP POST 请求会将其内容类型 HTTP 标头复制到消息的标头。​​contentType​

对于不存在此类标头的情况,框架依赖于默认内容类型。​​application/json​

内容类型与参数类型

如前所述,要使框架选择适当的框架,它需要参数类型和内容类型信息(可选)。 选择适当的逻辑驻留在参数解析器中,该解析器在调用用户定义的解析之前触发 函数(当框架知道实际参数类型时)。 如果参数类型与当前有效负载的类型不匹配,则框架将委托给 预配置以查看其中任何一个是否可以转换有效负载。​​MessageConverter​​​​MessageConverter​​​​MessageConverters​

和参数类型的组合是一种机制,框架通过该机制确定消息是否可以通过定位转换为目标类型。 适当的。 如果未找到 appropriateis,则会抛出异常,您可以通过添加自定义(请参阅)来处理该异常。​​contentType​​​​MessageConverter​​​​MessageConverter​​​​MessageConverter​​​​User-defined Message Converters​

不要指望仅基于转换为其他类型。 请记住,这是对目标类型的补充。 这是一个提示,可能会也可能不会考虑。​​Message​​​​contentType​​​​contentType​​​​MessageConverter​

消息转换器

​MessageConverters​​定义两个方法:

Object fromMessage(Message<?> message, Class<?> targetClass);

Message<?> toMessage(Object payload, @Nullable MessageHeaders headers);

了解这些方法的契约及其用法非常重要,特别是在Spring Cloud Stream的上下文中。

该方法将传入转换为参数类型。 的有效载荷可以是任何类型的,它是 直至实际实现支持多种类型。​​fromMessage​​​​Message​​​​Message​​​​MessageConverter​

提供的消息转换器

如前所述,该框架已经提供了一个堆栈来处理最常见的用例。 以下列表按优先级顺序描述了所提供的内容(使用第一个有效的):​​MessageConverters​​​​MessageConverters​​​​MessageConverter​

  1. ​JsonMessageConverter​​:支持在使用杰克逊或 Gson 库时转换 POJO 的有效负载(默认)。MessagecontentTypeapplication/json
  2. ​ByteArrayMessageConverter​​:支持在何时转换 fromtofor 情况的有效负载。它本质上是一个直通,主要是为了向后兼容而存在的。Messagebyte[]byte[]contentTypeapplication/octet-stream
  3. ​StringMessageConverter​​:支持将任何类型的转换为 awhenis。StringcontentTypetext/plain

当找不到合适的转换器时,框架将引发异常。发生这种情况时,您应该检查您的代码和配置,并确保这样做 不会遗漏任何内容(即,确保使用绑定或标头提供 Aby)。 但是,最有可能的是,您发现了一些不常见的情况(例如自定义也许),并且当前提供的堆栈不知道如何转换。如果是这种情况,您可以添加自定义。请参阅用户定义的消息转换器。​​contentType​​​​contentType​​​​MessageConverters​​​​MessageConverter​

用户定义的消息转换器

Spring Cloud 函数公开了一种定义和注册其他的机制。 要使用它,请实现,将其配置为 a。 然后,它被附加到现有的“消息转换器”堆栈中。​​MessageConverters​​​​org.springframework.messaging.converter.MessageConverter​​​​@Bean​

请务必了解自定义实现已添加到现有堆栈的头部。 因此,自定义实现优先于现有实现,这使您可以覆盖和添加到现有转换器。​​MessageConverter​​​​MessageConverter​

下面的示例演示如何创建消息转换器 Bean 以支持名为 的新内容类型:​​application/bar​

@SpringBootApplication
public static class SinkApplication {

...

@Bean
public MessageConverter customMessageConverter() {
return new MyCustomMessageConverter();
}
}

public class MyCustomMessageConverter extends AbstractMessageConverter {

public MyCustomMessageConverter() {
super(new MimeType("application", "bar"));
}

@Override
protected boolean supports(Class<?> clazz) {
return (Bar.class.equals(clazz));
}

@Override
protected Object convertFromInternal(Message<?> message, Class<?> targetClass, Object conversionHint) {
Object payload = message.getPayload();
return (payload instanceof Bar ? payload : new Bar((byte[]) payload));
}
}

关于 JSON 选项的说明

在Spring Cloud Function中,我们支持Jackson和Gson机制来处理JSON。 为了您的利益,已经将其抽象出来,根据它自己知道两种机制,并将使用选定的一种机制 由您或遵循默认规则。 默认规则如下:​​org.springframework.cloud.function.json.JsonMapper​

  • 无论类路径上的哪个库都是要使用的机制。因此,如果你必须使用类路径,Jackson将被使用,如果你有,那么Gson将被使用。com.fasterxml.jackson.*com.google.code.gson
  • 如果两者都有,则 Gson 将是默认值,或者您可以使用两个值之一设置属性:or。spring.cloud.function.preferred-json-mappergsonjackson

也就是说,类型转换通常对开发人员是透明的,但是考虑到它也注册为bean。 如果需要,您可以轻松地将其注入到代码中。​​org.springframework.cloud.function.json.JsonMapper​

Kotlin Lambda 支持

我们还提供对 Kotlin lambda 的支持(从 v2.0 开始)。 请考虑以下事项:

@Bean
open fun kotlinSupplier(): () -> String {
return { "Hello from Kotlin" }
}

@Bean
open fun kotlinFunction(): (String) -> String {
return { it.toUpperCase() }
}

@Bean
open fun kotlinConsumer(): (String) -> Unit {
return { println(it) }
}

上面代表配置为 Spring bean 的 Kotlin lambda。每个签名映射到 Java 等效的签名,因此框架支持/识别签名。 虽然 Kotlin 到 Java 映射的机制超出了本文档的范围,但重要的是要了解 此处也适用于“Java 8 函数支持”部分中概述的签名转换规则。​​Supplier​​​​Function​​​​Consumer​

要启用 Kotlin 支持,您只需在类路径上添加 Kotlin SDK 库,这将触发相应的 自动配置和支持类。

功能组件扫描

Spring Cloud 函数将扫描实现,并在调用的包中调用它是否存在。使用这个 功能,您可以编写不依赖于 Spring 的函数 - 甚至不需要注释。如果要使用不同的 包,你可以设置。您也可以用于完全关闭扫描。​​Function​​​​Consumer​​​​Supplier​​​​functions​​​​@Component​​​​spring.cloud.function.scan.packages​​​​spring.cloud.function.scan.enabled=false​

独立 Web 应用程序

函数可以自动导出为 HTTP 端点。

该模块具有自动配置 当它包含在 Spring 引导 Web 应用程序中时激活(使用 MVC 支持)。还有阿托 收集所有可选的依赖项,以防您只需要一个简单的 入门体验。​​spring-cloud-function-web​​​​spring-cloud-starter-function-web​

激活 Web 配置后,您的应用程序将具有 MVC 端点(默认在“/”上,但可配置)可用于访问 应用程序上下文中的函数,其中函数名称成为 URL 路径的一部分。支持的内容类型包括 纯文本和 JSON。​​spring.cloud.function.web.path​

方法

路径

请求

响应

地位

获取

/{供应商}

-

来自指定供应商的物料

200 OK

发布

/{消费者}

JSON 对象或文本

镜像输入并将请求正文推送到使用者

202 已接受

发布

/{消费者}

JSON 数组或带有新行的文本

镜像输入,将身体逐一推入消费者

202 已接受

发布

/{函数}

JSON 对象或文本

应用命名函数的结果

200 OK

发布

/{函数}

JSON 数组或带有新行的文本

应用命名函数的结果

200 OK

获取

/{功能}/{项目}

-

将项目转换为对象并返回应用函数的结果

200 OK

如上表所示,终结点的行为取决于方法以及传入请求数据的类型。当传入的数据是单值的,并且目标函数被声明为明显的单值(即不返回集合或)时,响应也将包含单个值。 对于多值响应,客户端可以通过发送“接受:文本/事件流”来请求服务器发送的事件流。​​Flux​

使用输入和输出 in 声明的函数和使用者会将请求标头视为消息标头,输出消息标头将转换为 HTTP标头。 消息的有效负载将为 aor 空字符串(如果有 noor 则为 null)。​​Message<?>​​​​body​​​​body​

发布文本时,Spring Boot 2.0 和旧版本的响应格式可能不同,具体取决于内容协商(提供内容类型并接受标头以获得最佳结果)。

请参阅测试功能应用程序以查看有关如何测试此类应用程序的详细信息和示例。

HTTP 请求参数

正如您从上表中注意到的那样,您可以将参数作为路径变量传递给函数(即)。 例如,将导致调用函数,其输入参数为。​​/{function}/{item}​​​​http://localhost:8080/uppercase/foo​​​​uppercase​​​​foo​

虽然这是推荐的方法,并且适合大多数用例,但有时您必须处理 HTTP 请求参数(例如,) 框架将处理类似于 HTTP 标头的 HTTP 请求参数,方法是将它们存储在标头键下的标头中,其值为 aof 请求参数,因此为了访问它们,您的函数输入签名应该 accepttype(例如,)。为方便起见,我们提供恒定。​​http://localhost:8080/uppercase/foo?name=Bill​​​​Message​​​​http_request_param​​​​Map​​​​Message​​​​Function<Message<String>, String>​​​​HeaderUtils.HTTP_REQUEST_PARAM​

函数映射规则

如果目录中只有一个函数(使用者等),则路径中的名称是可选的。 换句话说,在目录中只提供函数,调用是相同的。​​uppercase​​​​curl -H "Content-Type: text/plain" localhost:8080/uppercase -d hello​​​​curl -H "Content-Type: text/plain" localhost:8080/ -d hello​

可以使用管道或逗号来分隔函数名称来寻址复合函数(管道在 URL 路径中是合法的,但在命令行上键入有点尴尬)。 例如。​​curl -H "Content-Type: text/plain" localhost:8080/uppercase,reverse -d hello​

对于目录中有多个函数的情况,将导出每个函数并映射函数名称为 路径的一部分(例如,)。 在这种情况下,您仍然可以通过提供属性将特定函数或函数组合映射到根路径​​localhost:8080/uppercase​​​​spring.cloud.function.definition​

例如

--spring.cloud.function.definition=foo|bar

上述属性将组成 'foo' 和 'bar' 函数,并将组合函数映射到 “/” 路径。

相同的属性也适用于无法通过 URL 解析函数的情况。例如,您的 URL 可能是,但没有功能。 但是有功能和。所以,在这种情况下会决心。 这对于使用 URL 来传达某些信息的情况尤其有用,因为会用值调用消息标头 的实际 URL,使用户能够使用它进行评估和计算。​​localhost:8080/uppercase​​​​uppercase​​​​foo​​​​bar​​​​localhost:8080/uppercase​​​​foo|bar​​​​uri​

函数过滤规则

在目录中有多个函数的情况下,可能只需要导出某些函数或函数组合。在这种情况下,您可以使用 您打算导出的相同属性列表函数以分隔。 请注意,在这种情况下,不会将任何内容映射到根路径,并且不会导出未列出的函数(包括组合)​​spring.cloud.function.definition​​​​;​

例如

--spring.cloud.function.definition=foo;bar

这将仅导出函数和函数无论目录中有多少个可用功能(例如,)。​​foo​​​​bar​​​​localhost:8080/foo​

--spring.cloud.function.definition=foo|bar;baz

这将仅导出函数组合和函数,无论目录中有多少个可用函数(例如,)。​​foo|bar​​​​baz​​​​localhost:8080/foo,bar​

独立流式处理应用程序

要从代理(如RabbitMQ或Kafka)发送或接收消息,您可以利用project及其与Spring Cloud Function的集成。 请参考《春云流参考手册》的春云功能章节,了解更多详情和示例。​​spring-cloud-stream​

部署打包函数

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

标准的入口点是添加到类路径中,部署程序启动并查找一些配置来告诉它在哪里可以找到函数 jar。​​spring-cloud-function-deployer​

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

用户至少必须提供包含以下内容的存档的 URL 或资源位置 函数。它可以选择使用前缀通过依赖项查找来查找工件(有关完整详细信息,请参阅)。Spring 引导应用程序是从 jar 文件中引导的,使用 thes 来定位一个启动类,所以 例如,标准的弹簧靴胖罐效果很好。如果目标 jar 可以成功启动,则结果是一个函数 在主应用程序中注册。注册的函数可以通过主应用程序中的代码应用,即使 它是在一个独立的类加载器(由 Deault)中创建的。​​spring.cloud.function.location​​​​maven:​​​​FunctionProperties​​​​MANIFEST.MF​​​​FunctionCatalog​

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

@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​

@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");
}
}

请记住,Maven资源(例如本地和远程存储库,用户,密码等)是使用默认的MavenProperties解析的,这些资源是使用默认的MavenProperties解析的。 有效地使用本地默认值,并且在大多数情况下都有效。但是,如果您需要自定义,您只需提供一个类型的 bean,您可以在其中设置其他属性(请参阅下面的示例)。​​MavenProperties​

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

支持的打包方案

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

简单 JAR

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

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

您需要做的就是在部署此类包时指定属性:​​location​​​​function-class​

--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​​​​;​

例如

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

在这里,我们确定了两个要部署的函数,现在我们可以在函数目录中按名称(例如,)访问它们。​​catalog.lookup("reverseFunction");​

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

  • 组件扫描*

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

弹簧启动 JAR

此打包选项意味着存在对 Spring Boot 的依赖关系,并且 JAR 是作为 Spring Boot JAR 生成的。也就是说,鉴于部署的 JAR 在隔离的类装入器中运行,不会与实际部署程序使用的 Spring 引导版本发生任何版本冲突。 例如;考虑这样的JAR包含以下类(如果Spring/Spring Boot在类路径上,它可能有一些额外的Spring依赖项):

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

和以前一样,您需要做的就是在部署此类包时指定属性:​​location​​​​function-class​

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

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

弹簧启动应用程序

此打包选项意味着您的 JAR 是完整的独立 Spring 引导应用程序,具有托管 Spring bean 的功能。 和以前一样,有一个明显的假设,即存在对Spring Boot的依赖,并且JAR是作为Spring Boot JAR生成的。也就是说,鉴于部署的 JAR 在隔离的类装入器中运行,不会与实际部署程序使用的 Spring 引导版本发生任何版本冲突。 例如;考虑此类 JAR 包含以下类:

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​

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

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

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

​函数 Bean 定义​

Spring Cloud 函数支持需要快速启动的小应用的“功能”风格的 Bean 声明。Bean 声明的函数式风格是 Spring Framework 5.0 的一个特性,在 5.1 中得到了显著的增强。

​比较函数与传统 Bean 定义​

这是一个来自 熟悉和声明风格:​​@Configuration​​​​@Bean​

@SpringBootApplication
public class DemoApplication {

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

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

}

现在对于函数式 bean:用户应用程序代码可以重新转换为“函数式” 形式,像这样:

@SpringBootConfiguration
public class DemoApplication implements ApplicationContextInitializer<GenericApplicationContext> {

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

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

@Override
public void initialize(GenericApplicationContext context) {
context.registerBean("demo", FunctionRegistration.class,
() -> new FunctionRegistration<>(uppercase())
.type(FunctionTypeUtils.functionType(String.class, String.class)));
}

}

主要区别是:

  • 主类是 an。ApplicationContextInitializer
  • 方法已转换为对@Beancontext.registerBean()
  • 已替换为表示我们不启用 Spring 启动自动配置,但仍将类标记为“条目” 点”。@SpringBootApplication@SpringBootConfiguration
  • Thefrom Spring Boot 已被替换为 afrom Spring Cloud Function(它是一个 子类)。SpringApplicationFunctionalSpringApplication

在 Spring 云函数应用中注册的业务逻辑 Bean 为类型。 这是一个包装器,其中包含有关输入和输出类型的函数和信息。在应用程序的形式中,信息可以反射性地派生,但是在功能Bean注册中一些 除非我们使用 A,否则它会丢失。​​FunctionRegistration​​​​@Bean​​​​FunctionRegistration​

使用anandis制作应用程序的替代方法 本身实现(OROR)。示例(等效于上述):​​ApplicationContextInitializer​​​​FunctionRegistration​​​​Function​​​​Consumer​​​​Supplier​

@SpringBootConfiguration
public class DemoApplication implements Function<String, String> {

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

@Override
public String apply(String value) {
return value.toUpperCase();
}

}

如果您添加一个单独的、独立的类型类并将其注册到 使用方法的替代形式。最主要的是通用的 类型信息在运行时通过类声明提供。​​Function​​​​SpringApplication​​​​run()​

假设你有

@Component
public class CustomFunction implements Function<Flux<Foo>, Flux<Bar>> {
@Override
public Flux<Bar> apply(Flux<Foo> flux) {
return flux.map(foo -> new Bar("This is a Bar object from Foo value: " + foo.getValue()));
}

}

您将其注册为:

@Override
public void initialize(GenericApplicationContext context) {
context.registerBean("function", FunctionRegistration.class,
() -> new FunctionRegistration<>(new CustomFunction()).type(CustomFunction.class));
}

函数式 Bean 声明的局限性

与整个Spring Boot相比,大多数Spring Cloud Function应用程序的范围相对较小, 因此,我们能够轻松地使其适应这些函数式 Bean 定义。如果你走出这个有限的范围, 可以通过切换回样式配置或使用混合配置来扩展 Spring 云函数应用程序 方法。如果要利用 Spring 引导自动配置与外部数据存储集成, 例如,您将需要使用。您的函数仍然可以使用函数式定义 如果需要(即“混合”样式),但在这种情况下,您需要明确关闭“完整”(full 功能模式“使用以便 Spring 启动可以收回控制权。​​@Bean​​​​@EnableAutoConfiguration​​​​spring.functional.enabled=false​

功能可视化和控制

Spring 云功能支持通过执行器端点以及编程方式实现可用功能的可视化。​​FunctionCatalog​

程序化方式

若要以编程方式查看应用程序上下文中可用的函数,只需访问即可。在那里你可以 查找获取目录大小的方法,查找函数以及列出所有可用函数的名称。​​FunctionCatalog​

例如

FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class);
int size = functionCatalog.size(); // will tell you how many functions available in catalog
Set<String> names = functionCatalog.getNames(null); will list the names of all the Function, Suppliers and Consumers available in catalog
. . .
驱动器

由于执行器和 Web 是可选的,因此必须首先添加其中一个 Web 依赖项,并手动添加执行器依赖项。 下面的示例演示如何为 Web 框架添加依赖项:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

以下示例演示如何为 WebFlux 框架添加依赖项:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

您可以按如下方式添加执行器依赖项:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

还必须通过设置以下属性来启用执行器终结点:​​functions​​​​--management.endpoints.web.exposure.include=functions​

访问以下 URL 以查看函数目录中的函数:​​http://<host>:<port>/actuator/functions​

例如

curl http://localhost:8080/actuator/functions

您的输出应如下所示:

{"charCounter":
{"type":"FUNCTION","input-type":"string","output-type":"integer"},
"logger":
{"type":"CONSUMER","input-type":"string"},
"functionRouter":
{"type":"FUNCTION","input-type":"object","output-type":"object"},
"words":
{"type":"SUPPLIER","output-type":"string"}. . .

测试功能应用程序

Spring Cloud Function还有一些用于集成测试的实用程序,对于Spring Boot用户来说非常熟悉。

假设这是您的应用程序:

@SpringBootApplication
public class SampleFunctionApplication {

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

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

以下是包装此应用程序的 HTTP 服务器的集成测试:

@SpringBootTest(classes = SampleFunctionApplication.class,
webEnvironment = WebEnvironment.RANDOM_PORT)
public class WebFunctionTests {

@Autowired
private TestRestTemplate rest;

@Test
public void test() throws Exception {
ResponseEntity<String> result = this.rest.exchange(
RequestEntity.post(new URI("/uppercase")).body("hello"), String.class);
System.out.println(result.getBody());
}
}

或者当使用函数 Bean 定义样式时:

@FunctionalSpringBootTest
public class WebFunctionTests {

@Autowired
private TestRestTemplate rest;

@Test
public void test() throws Exception {
ResponseEntity<String> result = this.rest.exchange(
RequestEntity.post(new URI("/uppercase")).body("hello"), String.class);
System.out.println(result.getBody());
}
}

此测试与您为同一应用程序的版本编写的测试几乎相同 - 唯一的区别 是注释,而不是常规注释。所有其他作品, 像,是标准的 Spring 启动功能。​​@Bean​​​​@FunctionalSpringBootTest​​​​@SpringBootTest​​​​@Autowired​​​​TestRestTemplate​

为了帮助正确的依赖关系,这里是POM的摘录

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.13</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
. . . .
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-web</artifactId>
<version>3.2.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

或者,您可以仅使用 为非 HTTP 应用程序编写测试。例如:​​FunctionCatalog​

@FunctionalSpringBootTest
public class FunctionalTests {

@Autowired
private FunctionCatalog catalog;

@Test
public void words() {
Function<String, String> function = catalog.lookup(Function.class,
"uppercase");
assertThat(function.apply("hello")).isEqualTo("HELLO");
}

}

​无服务器平台适配器​

除了能够作为独立进程运行外,Spring Cloud还具有 功能应用程序可以适配运行现有的一个 无服务器平台。在项目中,有适用于 AWS 的适配器​​ Lambda​​,​​Azure​​, 和​​阿帕奇 打开拂鼠​​。​​Oracle Fn平台​​有自己的Spring Cloud Function适配器。​​Riff​​支持Java函数及其​​Java函数 Invoker​​原生行为是 Spring Cloud Function jar 的适配器。

​AWS Lambda​

AWS 适配器采用 Spring Cloud Function 应用程序,并将其转换为可在​​AWS​​Lambda 中运行的形式。

有关如何使用 AWS Lambda 的详细信息超出了本文档的范围,因此期望用户对 AWS 和 AWS Lambda,并希望了解 Spring 提供的额外价值。

​开始​

Spring Cloud Function框架的目标之一是提供必要的基础设施元素,使简单的函数应用程序能够在特定环境中以某种方式进行交互。 一个简单的函数应用程序(在上下文或 Spring 中)是包含 Supplier、Function 或 Consumer 类型的 bean 的应用程序。 因此,对于AWS,这意味着一个简单的函数Bean应该以某种方式在AWS Lambda环境中被识别和执行。

让我们看一下这个例子:

@SpringBootApplication
public class FunctionConfiguration {

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

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

它展示了一个完整的 Spring Boot 应用程序,其中定义了函数 bean。有趣的是,从表面上看,这只是 另一个启动应用程序,但在 AWS 适配器的上下文中,它也是一个完全有效的 AWS Lambda 应用程序。无其他代码或配置 是必需的。您需要做的就是打包并部署它,所以让我们看看如何做到这一点。

为了简化起见,我们提供了一个可供构建和部署的示例项目,您可以在此处访问它。

您只需执行以生成 JAR 文件。所有必要的 maven 插件都已设置以生成 适当的 AWS 可部署 JAR 文件。(您可以在JAR 布局说明中阅读有关 JAR 布局的更多详细信息)。​​./mvnw clean package​

然后,您必须将 JAR 文件(通过 AWS 控制面板或 AWS CLI)上传到 AWS。

当询问处理程序时,您指定哪个是通用请求处理程序。​​org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest​

仅此而已。使用一些示例数据保存并执行函数,对于此函数,这些示例数据应为 函数将大写并返回的字符串。

虽然是AWS的通用实施,旨在完全 将您与 AWS Lambda API 的细节隔离开来,在某些情况下,您可能需要指定所需的特定 AWS 来使用。下一节将向您解释如何实现这一目标。​​org.springframework.cloud.function.adapter.aws.FunctionInvoker​​​​RequestHandler​​​​RequestHandler​

​AWS 请求处理程序​

适配器有几个可以使用的通用请求处理程序。最通用的是(以及我们在入门部分中使用的那个) 是AWS的实现。 用户无需执行任何其他操作,只需在部署函数时在 AWS 控制面板上将其指定为“处理程序”。 它将处理大多数情况,包括Kinesis,流媒体等。​​org.springframework.cloud.function.adapter.aws.FunctionInvoker​​​​RequestStreamHandler​

如果您的应用程序有多个类型等。然后,您可以通过配置属性或环境变量来选择要使用的一个。函数是从春云中提取的。如果您没有指定框架将尝试按照搜索顺序查找默认值,它首先搜索然后最后)。​​@Bean​​​​Function​​​​spring.cloud.function.definition​​​​FunctionCatalog​​​​spring.cloud.function.definition​​​​Function​​​​Consumer​​​​Supplier​

​AWS 函数路由​

Spring Cloud Function的核心功能之一是​​路由​​ - 能够根据用户提供的路由指令将一个特殊功能委派给其他功能。

在 AWS Lambda 环境中,此功能还有一个额外的好处,因为它允许您绑定单个函数(路由函数) 作为 AWS Lambda,因此是 API 网关的单个 HTTP 终端节点。因此,最终您只管理一个功能和一个端点,同时受益 来自许多可以成为应用程序一部分的功能。

提供的​​示例中​​提供了更多详细信息, 然而,很少有一般的事情值得一提。

默认情况下,只要您的应用程序中有多个函数,就会启用路由功能,因为无法确定要绑定为 AWS Lambda 的函数,因此默认绑定。 这意味着您需要做的就是提供路由指令,您可以使用​​多种机制来​​执行这些指令(有关更多详细信息,请参阅​​示例​​)。​​org.springframework.cloud.function.adapter.aws.FunctionInvoker​​​​RoutingFunction​

另请注意,由于 AWS 不允许在环境变量名称中使用 dotsand/或连字符“-”,因此您可以从启动支持中受益,只需替换 带下划线的点和带驼峰大小写的连字符。所以例如成为桑德成为。​​.​​​​spring.cloud.function.definition​​​​spring_cloud_function_definition​​​​spring.cloud.function.routing-expression​​​​spring_cloud_function_routingExpression​

​具有自定义运行时的 AWS 函数路由​

使用​​[自定义运行时]时,​​函数路由的工作方式相同。您所需要的只是指定为 AWS 处理程序,就像使用函数名称作为处理程序一样。​​functionRouter​

​关于JAR布局的说明​

在 Lambda 中运行时,您不需要 Spring Cloud Function Web 或 Stream 适配器,因此您可以 需要在创建发送到 AWS 的 JAR 之前排除这些。Lambda 应用程序必须是 阴影,但 Spring 启动独立应用程序没有,因此您可以使用 2 运行相同的应用程序 单独的罐子(根据样品)。示例应用程序创建 2 个 jar 文件,一个带有用于在 Lambda 中部署的分类器,另一个包含运行时的可执行(瘦)jar。Spring Cloud 函数将尝试从 JAR 文件中为您找到一个“主类” 清单,使用属性(将由 Spring 引导为您添加 工具(如果使用起始父级)。如果您的清单中没有,您可以 使用环境变量或系统属性将函数部署到 AWS。​​aws​​​​spring-cloud-function-web​​​​Start-Class​​​​Start-Class​​​​MAIN_CLASS​

如果您没有使用函数式 Bean 定义,而是依赖于 Spring Boot 的自动配置, 然后,必须将其他转换器配置为 Maven-shade-plugin 执行的一部分。

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.1</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>aws</shadedClassifierName>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<transformer implementation="org.springframework.boot.maven.PropertiesMergingResourceTransformer">
<resource>META-INF/spring.factories</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.components</resource>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>

​构建文件设置​

为了在AWS Lambda上运行Spring Cloud Function应用程序,您可以利用Maven或Gradle。 云平台提供商提供的插件。

​马文​

为了将适配器插件用于Maven,请将插件依赖项添加到您的文件中:​​pom.xml​

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-aws</artifactId>
</dependency>
</dependencies>

正如​​JAR 布局说明​​中所述,您需要一个带阴影的 jar 才能上传它 到 AWS Lambda。您可以使用​​Maven Shade插件​​。 可以在上面找到​​设置​​示例。

您可以使用Spring Boot Maven插件来生成​​薄罐​​子。

<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-thin-layout</artifactId>
<version>${wrapper.version}</version>
</dependency>
</dependencies>
</plugin>

您可以找到用于部署 Spring Cloud 函数的完整示例文件 ​​使用​​ Maven 向 AWS Lambda 提供应用程序。​​pom.xml​

​格拉德尔​

要使用 Gradle 的适配器插件,请将依赖项添加到您的文件中:​​build.gradle​

dependencies {
compile("org.springframework.cloud:spring-cloud-function-adapter-aws:${version}")
}

正如​​JAR 布局说明​​中所述,您需要一个带阴影的 jar 才能上传它 到 AWS Lambda。您可以使用​​Gradle Shadow 插件​​来实现:

buildscript {
dependencies {
classpath "com.github.jengelman.gradle.plugins:shadow:${shadowPluginVersion}"
}
}
apply plugin: 'com.github.johnrengelman.shadow'

assemble.dependsOn = [shadowJar]

import com.github.jengelman.gradle.plugins.shadow.transformers.*

shadowJar {
classifier = 'aws'
dependencies {
exclude(
dependency("org.springframework.cloud:spring-cloud-function-web:${springCloudFunctionVersion}"))
}
// Required for Spring
mergeServiceFiles()
append 'META-INF/spring.handlers'
append 'META-INF/spring.schemas'
append 'META-INF/spring.tooling'
transform(PropertiesFileTransformer) {
paths = ['META-INF/spring.factories']
mergeStrategy = "append"
}
}

您可以使用 Spring Boot Gradle 插件和 Spring Boot Thin Gradle 插件来生成 ​​薄罐​​子。

buildscript {
dependencies {
classpath("org.springframework.boot.experimental:spring-boot-thin-gradle-plugin:${wrapperVersion}")
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'org.springframework.boot'
apply plugin: 'org.springframework.boot.experimental.thin-launcher'
assemble.dependsOn = [thinJar]

您可以找到用于部署 Spring Cloud 函数的完整示例文件 使用 Gradle 向 AWS Lambda 提供应用程序,​​请点击此处​​。​​build.gradle​

​上传​

在下面构建示例并将jar文件上传到Lambda。处理程序可以是(类的 FQN,而不是方法引用,尽管 Lambda 确实接受方法引用)。​​spring-cloud-function-samples/function-sample-aws​​​​-aws​​​​example.Handler​​​​org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler​

./mvnw -U clean package

使用 AWS 命令行工具,它如下所示:

aws lambda create-function --function-name Uppercase --role arn:aws:iam::[USERID]:role/service-role/[ROLE] --zip-file fileb://function-sample-aws/target/function-sample-aws-2.0.0.BUILD-SNAPSHOT-aws.jar --handler org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler --description "Spring Cloud Function Adapter Example" --runtime java8 --region us-east-1 --timeout 30 --memory-size 1024 --publish

AWS 示例中函数的输入类型是具有名为“value”的单个属性的 Foo。所以你需要这个来测试它:

{
"value": "test"
}

AWS 示例应用程序以“功能”样式编写(作为 an)。这在 Lambda 中启动时比传统样式快得多,所以如果你不需要(或)它是一个不错的选择。热启动不受影响。​​ApplicationContextInitializer​​​​@Bean​​​​@Beans​​​​@EnableAutoConfiguration​

类型转换

Spring Cloud 函数将尝试透明地处理原始之间的类型转换 函数声明的输入流和类型。

例如,如果您的函数签名是这样的,我们将尝试转换 传入流事件到的实例。​​Function<Foo, Bar>​​​​Foo​

在事件类型未知或无法确定(例如,)的情况下,我们将尝试 将传入流事件转换为泛型。​​Function<?, ?>​​​​Map​

原始输入

有时您可能希望访问原始输入。在这种情况下,您所需要的只是声明您的 要接受的函数签名。例如。在这种情况下 我们不会尝试任何转换,而是将原始输入直接传递给函数。​​InputStream​​​​Function<InputStream, ?>​

微软 Azure

Azure 适配器启动 Spring 云函数上下文,并从Azure引导函数调用 框架到用户函数中,必要时使用 Spring 引导配置。Azure Functions 具有相当独特和 侵入性编程模型,涉及特定于 Azure 平台的用户代码中的批注。 但是,重要的是要了解,由于Spring Cloud Function提供的集成风格,特别是这种基于注释的编程模型只是一种类型安全的配置方式。 要识别为 Azure 函数的简单 Java 函数(无法识别 Azure 的函数)。 您需要做的就是创建一个扩展、定义和配置函数处理程序方法的处理程序,并且 进行回调方法。此处理程序方法将输入和输出类型作为带批注的方法参数提供 (使 Azure 能够检查类并创建 JSON 绑定)。​​org.springframework.cloud.function.adapter.azure.FunctionInvoker​​​​FunctionInvoker​​​​handleRequest(..)​

public class UppercaseHandler extends FunctionInvoker<Message<String>, String> {

@FunctionName("uppercase")
public String execute(@HttpTrigger(name = "req", methods = {HttpMethod.GET,
HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
ExecutionContext context) {
Message<String> message = MessageBuilder.withPayload(request.getBody().get()).copyHeaders(request.getHeaders()).build();
return handleRequest(message, context);
}
}

请注意,除了通过 Azure 注释提供配置的表单之外,我们在此处理程序方法的主体内创建一个实例,并对返回其结果的方法进行回调。​​Message​​​​handleRequest(..)​

您要延迟的实际用户功能如下所示

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

OR

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

请注意,在创建消息时,您可以有效地复制 HTTP 标头,以便在必要时使用它们。

该类有两个有用的 可以将实际函数调用委托给的方法(and),因此大多数情况下该函数将只有一行。​​org.springframework.cloud.function.adapter.azure.FunctionInvoker​​​​handleRequest​​​​handleOutput​

函数名称(定义)将从 Azure 的方法中检索,从而有效地支持应用程序上下文中的多个函数。​​ExecutionContext.getFunctionName()​

访问 Azure ExecutionContext

有时需要以 的形式访问 Azure 运行时提供的目标执行上下文。 例如,其中一个需求是日志记录,因此它可以显示在 Azure 控制台中。​​com.microsoft.azure.functions.ExecutionContext​

为此,FunctionInvoker 将添加一个实例作为消息标头,以便您可以通过 Trusted 检索它。​​ExecutionContext​​​​executionContext​

@Bean
public Function<Message<String>, String> uppercase(JsonMapper mapper) {
return message -> {
String value = message.getPayload();
ExecutionContext context = (ExecutionContext) message.getHeaders().get("executionContext");
. . .
}
}

关于JAR布局的说明

在 Azure 中运行时不需要 Spring Cloud Function Web,因此可以排除此 在创建部署到 Azure 的 JAR 之前,但如果包含它,则不会使用它,因此 把它留在里面也无妨。Azure 上的函数应用程序是由 Maven插件。该函数位于此项目生成的 JAR 文件中。 此示例使用精简布局将其创建为可执行 jar,以便 Azure 可以找到 处理程序类。如果您愿意,可以只使用常规的平面JAR文件。 不应包含依赖项。

构建文件设置

为了在Microsoft Azure上运行Spring Cloud Function应用程序,您可以利用Maven 云平台提供商提供的插件。

为了将适配器插件用于Maven,请将插件依赖项添加到您的文件中:​​pom.xml​

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-azure</artifactId>
</dependency>
</dependencies>

然后,配置插件。需要为你的 Azure 提供特定于 Azure 的配置 应用程序,指定 和其他可选属性,以及 添加目标执行,以便 Azure 所需的文件是 为您生成。完整的插件文档可以在插件存储库中找到。​​resourceGroup​​​​appName​​​​package​​​​function.json​

<plugin>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-functions-maven-plugin</artifactId>
<configuration>
<resourceGroup>${functionResourceGroup}</resourceGroup>
<appName>${functionAppName}</appName>
</configuration>
<executions>
<execution>
<id>package-functions</id>
<goals>
<goal>package</goal>
</goals>
</execution>
</executions>
</plugin>

您还必须确保插件要扫描的文件可以在 Azure 函数暂存目录(有关暂存目录及其默认位置的更多详细信息,请参阅插件存储库)。

您可以找到用于部署 Spring Cloud 函数的完整示例文件 应用程序到Microsoft Azure与Maven在这里。​​pom.xml​

到目前为止,只有Maven插件可用。Gradle 插件尚未由 云平台提供商。

./mvnw -U clean package

运行示例

您可以在本地运行示例,就像其他 Spring Cloud 函数示例一样:



和。​​curl -H "Content-Type: text/plain" localhost:8080/api/uppercase -d '{"value": "hello foobar"}'​

您将需要 CLI 应用程序(有关更多详细信息,请参阅https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-java-maven)。若要在 Azure 运行时上部署函数,请执行以下操作:​​az​

$ az login
$ mvn azure-functions:deploy

在另一个终端上尝试这个:。请确保为上述功能使用正确的 URL。或者,可以在 Azure 仪表板 UI 中测试函数(单击函数名称,转到右侧并单击“测试”,然后单击右下角的“运行”)。​​curl https://<azure-function-url-from-the-log>/api/uppercase -d '{"value": "hello foobar!"}'​

Azure 示例中函数的输入类型是具有名为“值”的单个属性的 Foo。所以你需要这个来测试它,如下所示:

{
"value": "foobar"
}

Azure 示例应用以“非功能性”样式(使用)编写。函数式样式(使用 justor)在 Azure 中启动时比传统样式快得多,因此,如果您不需要(或)这是一个不错的选择。热启动不受影响。 :分支:主​​@Bean​​​​Function​​​​ApplicationContextInitializer​​​​@Bean​​​​@Beans​​​​@EnableAutoConfiguration​

谷歌云函数

Google Cloud Functions 适配器使 Spring Cloud Function 应用程序能够在Google Cloud Functions无服务器平台上运行。 您可以使用开源的 Google Functions Framework for Java或 GCP 在本地运行该函数。

项目依赖关系

首先将依赖项添加到项目中。​​spring-cloud-function-adapter-gcp​

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-gcp</artifactId>
</dependency>

...
</dependencies>

此外,添加 which,这将构建要部署的函数的 JAR。​​spring-boot-maven-plugin​

请注意,我们还引用了 的依赖项。这是必要的,因为它会修改插件,以正确的JAR格式打包您的函数,以便在Google Cloud Functions上部署。​​spring-cloud-function-adapter-gcp​​​​spring-boot-maven-plugin​

<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<outputDirectory>target/deploy</outputDirectory>
</configuration>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-gcp</artifactId>
</dependency>
</dependencies>
</plugin>

最后,添加作为 Google Functions Framework for Java 的一部分提供的 Maven 插件。 这允许您通过以下方式在本地测试函数。​​mvn function:run​


函数目标应始终设置为;这是一个适配器类,充当从 Google Cloud Functions 平台进入 Spring Cloud Function 的入口点。​​org.springframework.cloud.function.adapter.gcp.GcfJarLauncher​

<plugin>
<groupId>com.google.cloud.functions</groupId>
<artifactId>function-maven-plugin</artifactId>
<version>0.9.1</version>
<configuration>
<functionTarget>org.springframework.cloud.function.adapter.gcp.GcfJarLauncher</functionTarget>
<port>8080</port>
</configuration>
</plugin>

可以在Spring Cloud Functions GCP示例中找到工作的完整示例。pom.xml

HTTP 函数

Google Cloud Functions 支持部署 HTTPFunctions,即由 HTTP 请求调用的函数。以下部分描述了将 Spring 云函数部署为 HTTP 函数的说明。

开始

让我们从一个简单的 Spring Cloud Function 示例开始:

@SpringBootApplication
public class CloudFunctionMain {

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

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

在中指定配置主类。​​resources/META-INF/MANIFEST.MF​

Main-Class: com.example.CloudFunctionMain

然后在本地运行该函数。 这是由项目依赖项部分中描述的 Google Cloud Functions 提供的。​​function-maven-plugin​

mvn function:run

调用 HTTP 函数:

curl http://localhost:8080/ -d "hello"
部署到基仕伯

首先打包应用程序。

mvn package

如果您添加了上面定义的自定义插件,您应该会看到生成的 JAR 目录。 此 JAR 的格式正确,可以部署到 Google Cloud Functions。​​spring-boot-maven-plugin​​​​target/deploy​

接下来,请确保您已安装云开发工具包 CLI。

从项目基目录中运行以下命令进行部署。

gcloud functions deploy function-sample-gcp-http \
--entry-point org.springframework.cloud.function.adapter.gcp.GcfJarLauncher \
--runtime java11 \
--trigger-http \
--source target/deploy \
--memory 512MB

调用 HTTP 函数:

curl https://REGION-PROJECT_ID.cloudfunctions.net/function-sample-gcp-http -d "hello"

后台函数

Google Cloud Functions 还支持部署后台函数,这些后台函数是间接调用的,以响应事件,例如有关 CloudPub/Sub主题的消息、Cloud Storage存储桶中的更改或Firebase事件。

还允许将函数部署为后台函数。​​spring-cloud-function-adapter-gcp​

以下部分介绍了编写云发布/订阅主题后台函数的过程。 但是,有许多不同的事件类型可以触发后台函数执行,此处不讨论;这些在后台函数触发器文档中进行了描述。

开始

让我们从一个简单的 Spring Cloud 函数开始,它将作为 GCF 后台函数运行:

@SpringBootApplication
public class BackgroundFunctionMain {

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

@Bean
public Consumer<PubSubMessage> pubSubFunction() {
return message -> System.out.println("The Pub/Sub message data: " + message.getData());
}
}

此外,在项目中创建类具有以下定义。 此类表示 Pub/Sub 事件结构,该结构在 Pub/Sub 主题事件上传递给函数。​​PubSubMessage​

public class PubSubMessage {

private String data;

private Map<String, String> attributes;

private String messageId;

private String publishTime;

public String getData() {
return data;
}

public void setData(String data) {
this.data = data;
}

public Map<String, String> getAttributes() {
return attributes;
}

public void setAttributes(Map<String, String> attributes) {
this.attributes = attributes;
}

public String getMessageId() {
return messageId;
}

public void setMessageId(String messageId) {
this.messageId = messageId;
}

public String getPublishTime() {
return publishTime;
}

public void setPublishTime(String publishTime) {
this.publishTime = publishTime;
}

}

在中指定配置主类。​​resources/META-INF/MANIFEST.MF​

Main-Class: com.example.BackgroundFunctionMain

然后在本地运行该函数。 这是由项目依赖项部分中描述的 Google Cloud Functions 提供的。​​function-maven-plugin​

mvn function:run

调用 HTTP 函数:

curl localhost:8080 -H "Content-Type: application/json" -d '{"data":"hello"}'

通过查看日志验证是否已调用该函数。

部署到基仕伯

要将后台函数部署到 GCP,请先打包应用程序。

mvn package

如果您添加了上面定义的自定义插件,您应该会看到生成的 JAR 目录。 此 JAR 的格式正确,可以部署到 Google Cloud Functions。​​spring-boot-maven-plugin​​​​target/deploy​

接下来,请确保您已安装云开发工具包 CLI。

从项目基目录中运行以下命令进行部署。

gcloud functions deploy function-sample-gcp-background \
--entry-point org.springframework.cloud.function.adapter.gcp.GcfJarLauncher \
--runtime java11 \
--trigger-topic my-functions-topic \
--source target/deploy \
--memory 512MB

Google Cloud Function 现在会在每次将消息发布到指定的主题时调用该函数。​​--trigger-topic​

有关测试和验证后台函数的演练,请参阅运行GCF 后台函数示例的说明。

示例函数

该项目提供以下示例函数作为参考:

  • 函数-sample-gcp-http是一个HTTP函数,您可以在本地测试并尝试部署。
  • 函数样本 gcp-background显示了一个后台函数的示例,该函数由发布到指定 Pub/Sub 主题的消息触发。