对于最新的稳定版本,请使用 spring-cloud-function 4.3.0! |
AWS Lambda
AWS 适配器采用 Spring Cloud Function 应用程序,并将其转换为可以在 AWS Lambda 中运行的形式。
通常,有两种方法可以在 AWS Lambda 上运行 Spring 应用程序:
-
通过 Spring Cloud Function 使用 AWS Lambda 适配器来实现如下所述的功能方法。这非常适合单职责 API 以及基于事件和消息传递的系统,例如处理来自 Amazon SQS 或 Amazon MQ 队列、Apache Kafka 流的消息,或对 Amazon S3 中的文件上传做出反应。
-
通过无服务器 Java 容器项目在 AWS Lambda 上运行 Spring Boot Web 应用程序。这非常适合将现有 Spring 应用程序迁移到 AWS Lambda,或者如果您构建具有多个 API 端点的复杂 API 并希望维护熟悉的
RestController
方法。这种方法在 Spring Boot Web 的无服务器 Java 容器中进行了更详细的概述。
以下指南希望您对 AWS 和 AWS Lambda 有基本的了解,并重点介绍 Spring 提供的附加价值。有关如何开始使用 AWS Lambda 的详细信息不在本文档的讨论范围内。如果您想了解更多信息,可以导航到基本的 AWS Lambda 概念或完整的 Java on AWS 概述。
开始
Spring Cloud Function 框架的目标之一是提供必要的基础设施元素,使简单的功能应用程序能够与特定环境(例如 AWS Lambda)兼容。
在 Spring 的上下文中,一个简单的函数式应用程序包含类型为Supplier
,Function
或Consumer
.
让我们看一下例子:
@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。从表面上看,这只是另一个 Spring Boot 应用程序。但是,当将 Spring Cloud Function AWS Adapter 添加到项目中时,它将成为完全有效的 AWS Lambda 应用程序:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-aws</artifactId>
</dependency>
</dependencies>
不需要其他代码或配置。我们提供了一个可供构建和部署的示例项目。您可以在官方的 Spring Cloud 函数示例存储库中访问它。
您只需执行mvn clean package
以生成 JAR 文件。所有必要的 maven 插件都已经设置好,可以生成
适当的 AWS 可部署 JAR 文件。(您可以在 JAR 布局的注释中阅读有关 JAR 布局的更多详细信息)。
AWS Lambda 函数处理程序
与通过给定 HTTP 端口上的侦听器(80、443)公开其功能的传统 Web 应用程序相比,AWS Lambda 函数是在预定义的入口点(称为 Lambda 函数处理程序)调用的。
我们建议使用内置的org.springframework.cloud.function.adapter.aws.FunctionInvoker
处理程序来简化与 AWS Lambda 的集成。它提供了高级功能,例如多功能路由、与 AWS 细节解耦以及开箱即用的 POJO 序列化。请参阅 AWS 请求处理程序和 AWS 函数路由部分以了解更多信息。
部署
构建应用程序后,您可以通过 AWS 控制台、AWS 命令行界面 (CLI) 或基础设施即代码 (IaC) 工具(例如 AWS Serverless Application Model (AWS SAM)、AWS Cloud Development Kit (AWS CDK)、AWS CloudFormation 或 Terraform) 手动部署 JAR 文件。
使用 AWS 控制台创建 Hello world Lambda 函数
-
选择创建函数。
-
从头开始选择“作者”。
-
对于函数名称,输入
MySpringLambdaFunction
. -
对于 Runtime,选择 Java 21。
-
选择创建函数。
要上传代码并测试函数,请执行以下作:
-
例如,上传之前创建的 JAR 文件
target/function-sample-aws-0.0.1-SNAPSHOT-aws.jar
. -
提供条目处理程序方法
org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest
. -
导航至“测试”选项卡,然后单击“测试”按钮。该函数应返回提供的大写 JSON 有效负载。


要使用基础设施即代码 (IaC) 工具自动部署,请参阅官方 AWS 文档。
AWS 请求处理程序
如入门部分所述,AWS Lambda 函数是在预定义的入口点(称为 Lambda 函数处理程序)处调用的。在最简单的形式中,这可以是一个 Java 方法引用。在上面的示例中,这将是com.my.package.FunctionConfiguration::uppercase
.需要此配置来建议 AWS Lambda 在提供的 JAR 中调用哪个 Java 方法。
调用 Lambda 函数时,它会将额外的请求有效负载和上下文对象传递给此处理程序方法。请求有效负载因触发该函数的 AWS 服务(Amazon API Gateway、Amazon S3、Amazon SQS、Apache Kafka 等)而异。上下文对象提供有关 Lambda 函数、调用和环境的其他信息,例如唯一的请求 ID(另请参阅官方文档中的 Java 上下文)。
AWS 提供预定义的处理程序接口(称为RequestHandler
或RequestStreamHandler
) 通过 aws-lambda-java-events 和 aws-lambda-java-core 库处理有效负载和上下文对象。
Spring Cloud Function 已经实现了这些接口,并提供了一个org.springframework.cloud.function.adapter.aws.FunctionInvoker
完全抽象函数代码
来自 AWS Lambda 的细节。这允许您根据运行函数的平台切换入口点。
但是,对于某些使用案例,您希望与 AWS 环境深度集成。例如,当您的函数由 Amazon S3 文件上传触发时,您可能希望访问特定的 Amazon S3 属性。或者,如果您想在处理 Amazon SQS 队列中的项目时返回部分批处理响应。在这种情况下,您仍然可以利用通用org.springframework.cloud.function.adapter.aws.FunctionInvoker
但您将在函数代码中使用专用的 AWS 对象:
@Bean
public Function<S3Event, String> processS3Event() {}
@Bean
public Function<SQSEvent, SQSBatchResponse> processSQSEvent() {}
AWS 函数路由
Spring Cloud Function 的核心特性之一是路由。此功能允许您使用一个特殊的 Java 方法(充当 Lambda 函数处理程序)委托给其他内部方法。当通用FunctionInvoker
自动将请求路由到您的uppercase
功能。
默认情况下,如果您的应用有多个@Bean
类型Function
等等,它们是从 Spring Cloud 中提取的FunctionCatalog
框架将尝试按照它首先搜索的搜索顺序查找默认值Function
然后Consumer
最后Supplier
.需要这些默认路由功能,因为FunctionInvoker
无法确定要绑定哪个函数,因此内部默认为RoutingFunction
.建议使用多种机制提供其他路由指令(有关更多详细信息,请参阅示例)。
正确的路由机制取决于您将 Spring Cloud Function 项目部署为单个或多个 Lambda 函数的偏好。
单一功能与多种功能
如果您在同一个 Spring Cloud Function 项目中实现多个 Java 方法,例如uppercase
和lowercase
,您可以部署两个单独的 Lambda 函数,其中包含静态路由信息,或者提供动态路由方法来决定在运行时调用哪个方法。让我们看看这两种方法。
-
如果每个函数的扩展、配置或权限要求不同,则部署两个单独的 AWS Lambda 函数是有意义的。例如,如果您创建了两个 Java 方法
readObjectFromAmazonS3
和writeToAmazonDynamoDB
在同一个 Spring Cloud Function 项目中,您可能希望创建两个单独的 Lambda 函数。这是因为它们需要不同的权限才能与 S3 或 DynamoDB 通信,或者它们的负载模式和内存配置差异很大。通常,对于从流或队列读取的基于消息传递的应用程序,也建议使用此方法,因为每个 Lambda 事件源映射都有专用配置。 -
当多个 Java 方法共享相同的权限集或提供一致的业务功能时,单个 Lambda 函数是一种有效的方法。例如,基于 CRUD 的 Spring Cloud Function 项目具有
createPet
,updatePet
,readPet
和deletePet
所有方法都与同一个 DynamoDB 表通信,并且具有相似的使用模式。使用单个 Lambda 函数将提高共享类的部署简单性、内聚性和代码重用性(PetEntity
).此外,它可以减少顺序调用之间的冷启动,因为readPet
其次writePet
很可能会命中已经运行的 Lambda 执行环境。但是,当您构建更复杂的 API 时,或者您想利用@RestController
方法,您可能还想评估 Serverless Java container for Spring Boot Web 选项。
如果您喜欢第一种方法,您还可以创建两个单独的 Spring Cloud Function 项目并单独部署它们。如果不同的团队负责维护和部署功能,这可能会很有用。但是,在这种情况下,您需要处理在它们之间共享跨领域问题,例如帮助程序方法或实体类。一般来说,我们建议将与传统的基于 Web 的应用程序相同的软件模块化原则应用于功能项目。有关如何选择正确方法的更多信息,您可以参考比较无服务器微服务的设计方法。
做出决定后,您可以从以下路由机制中受益。
多个 Lambda 函数的路由
如果您决定将单个 Spring Cloud Function 项目 (JAR) 部署到多个 Lambda 函数,则需要提供有关要调用哪个特定方法的提示,例如uppercase
或lowercase
.您可以使用 AWS Lambda 环境变量来提供路由说明。
请注意,AWS 不允许使用点.
和/或环境变量名称中的连字符。您可以受益于 Spring Boot 支持,只需用下划线替换点,用驼形命名法替换连字符即可。所以比如说-
spring.cloud.function.definition
成为spring_cloud_function_definition
和spring.cloud.function.routing-expression
成为spring_cloud_function_routingExpression
.
因此,单个 Spring Cloud 项目的配置将两种方法部署到单独的 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();
}
@Bean
public Function<String, String> lowercase() {
return value -> value.toLowerCase();
}
}
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
MyUpperCaseLambda:
Type: AWS::Serverless::Function
Properties:
Handler: org.springframework.cloud.function.adapter.aws.FunctionInvoker
Runtime: java21
MemorySize: 512
CodeUri: target/function-sample-aws-0.0.1-SNAPSHOT-aws.jar
Environment:
Variables:
spring_cloud_function_definition: uppercase
MyLowerCaseLambda:
Type: AWS::Serverless::Function
Properties:
Handler: org.springframework.cloud.function.adapter.aws.FunctionInvoker
Runtime: java21
MemorySize: 512
CodeUri: target/function-sample-aws-0.0.1-SNAPSHOT-aws.jar
Environment:
Variables:
spring_cloud_function_definition: lowercase
您可能会问 - 为什么不使用 Lambda 函数处理程序并将入口方法直接指向uppercase
和lowercase
?在 Spring Cloud Function 项目中,建议使用内置的FunctionInvoker
如 AWS 请求处理程序中所述。因此,我们通过环境变量提供路由定义。
在单个 Lambda 函数内路由
如果您决定使用多种方法(uppercase
或lowercase
) 添加到单个 Lambda 函数,您需要一种更动态的路由方法。因为application.properties
环境变量是在生成或部署时定义的,不能将它们用于单个函数方案。在这种情况下,您可以利用MessagingRoutingCallback
或Message Headers
如 Spring Cloud 函数路由部分所述。
提供的示例中提供了更多详细信息。
性能注意事项
Serverless Functions 的一个核心特征是能够扩展到零并处理突然的流量高峰。为了处理请求,AWS Lambda 会启动新的执行环境。这些环境需要初始化,需要下载代码并启动 JVM + 应用程序。这也称为冷启动。若要减少此冷启动时间,可以依靠以下机制来优化性能。
-
利用 AWS Lambda SnapStart 从预先初始化的快照启动 Lambda 函数。
-
通过 AWS Lambda Power Tuning 调整内存配置,以在性能和成本之间找到最佳权衡。
-
遵循 AWS 开发工具包最佳实践,例如在处理程序代码之外定义开发工具包客户端,或利用更高级的启动技术。
-
实现额外的 Spring 机制以减少 Spring 启动和初始化时间,例如功能 Bean 注册。
有关更多信息,请参阅官方指南。
GraalVM 本机映像
Spring Cloud Function 为在 AWS Lambda 上运行的函数提供 GraalVM Native Image 支持。由于 GraalVM 本机映像不在传统的 Java 虚拟机 (JVM) 上运行,因此您必须将本机 Spring Cloud Function 部署到 AWS Lambda 自定义运行时。最显着的区别是,您不再提供 JAR 文件,而是提供原生映像和引导文件,其中包含捆绑在 zip 包中的启动指令:
lambda-custom-runtime.zip
|-- bootstrap
|-- function-sample-aws-native
Bootstrap 文件:
#!/bin/sh
cd ${LAMBDA_TASK_ROOT:-.}
./function-sample-aws-native
自定义运行时
Lambda 专注于提供稳定的长期支持 (LTS) Java 运行时版本。官方 Lambda 运行时是围绕作系统、编程语言和软件库的组合构建的,这些软件库需要进行维护和安全更新。例如,Java 的 Lambda 运行时支持 LTS 版本,例如 Java 17 Corretto 和 Java 21 Corretto。你可以在这里找到完整的列表。没有为 Java 22、Java 23 或 Java 24 等非 LTS 版本提供运行时。
要使用其他语言版本、JVM 或 GraalVM 本机映像,Lambda 允许您创建自定义运行时。自定义运行时允许您提供和配置自己的运行时来运行其应用程序代码。Spring Cloud Function 提供了所有必要的组件来简化它。
从代码的角度来看,该应用程序看起来不应与任何其他 Spring Cloud Function 应用程序不同。
您唯一需要做的就是提供一个bootstrap
运行 Spring Boot 应用程序的 ZIP/JAR 根目录中的脚本。
并在 AWS 中创建函数时选择“自定义运行时”。
这是一个示例“bootstrap”文件:
#!/bin/sh
cd ${LAMBDA_TASK_ROOT:-.}
java -Dspring.main.web-application-type=none -Dspring.jmx.enabled=false \
-noverify -XX:TieredStopAtLevel=1 -Xss256K -XX:MaxMetaspaceSize=128M \
-Djava.security.egd=file:/dev/./urandom \
-cp .:`echo lib/*.jar | tr ' ' :` com.example.LambdaApplication
这com.example.LambdaApplication
表示包含函数 bean 的应用程序。
将 AWS 中的处理程序名称设置为函数的名称。您也可以在此处使用函数组合(例如uppercase|reverse
).
将 ZIP/JAR 上传到 AWS 后,您的函数将在自定义运行时中运行。
我们提供了一个示例项目,您还可以在其中了解如何配置 POM 以正确生成 ZIP 文件。
函数式 bean 定义样式也适用于自定义运行时,并且是
比@Bean
风格。自定义运行时的启动速度甚至比功能型 Bean 实现快得多
Java lambda 的 - 这主要取决于您需要在运行时加载的类数量。
Spring 在这里没有做太多事情,所以你可以通过在函数中仅使用原始类型来减少冷启动时间,例如
并且不做任何定制工作@PostConstruct
初始值设定项。
使用自定义运行时的 AWS 函数路由
使用自定义运行时函数路由时,其工作方式相同。您只需要指定functionRouter
作为 AWS Handler 的方式与使用函数名称作为处理程序的方式相同。
将 Lambda 函数部署为容器映像
与基于 JAR 或 ZIP 的部署相比,您还可以通过镜像注册表将 Lambda 函数部署为容器镜像。有关更多详细信息,请参阅官方 AWS Lambda 文档。
以类似于此处描述的方式部署容器映像时,重要的是
记住设置和环境变量DEFAULT_HANDLER
替换为函数的名称。
例如,对于下面显示的函数 beanDEFAULT_HANDLER
值将为readMessageFromSQS
.
@Bean
public Consumer<Message<SQSMessageEvent>> readMessageFromSQS() {
return incomingMessage -> {..}
}
此外,重要的是要记住确保spring_cloud_function_web_export_enabled
也设置为false
.是的true
默认情况下。
JAR 布局说明
在 Lambda 中运行时不需要 Spring Cloud Function Web 或 Stream 适配器,因此您可能
在创建发送到 AWS 的 JAR 之前,需要排除这些内容。Lambda 应用程序必须是
shaded,但 Spring Boot 独立应用程序没有,因此您可以使用 2
单独的罐子(根据
Samples)。示例应用创建 2 个 jar 文件,其中一个文件带有aws
用于在 Lambda 中部署的分类器,以及一个包含spring-cloud-function-web
在运行时。Spring Cloud Function 将尝试从 JAR 文件中为您找到一个“主类”
manifest,使用Start-Class
属性(将由 Spring Boot
工具(如果使用入门父级)。如果没有Start-Class
在您的清单中,您可以
使用环境变量或系统属性MAIN_CLASS
当您将函数部署到 AWS 时。
如果您没有使用功能 Bean 定义,而是依赖于 Spring Boot 的自动配置,
并且不依赖于spring-boot-starter-parent
,
然后必须将其他转换器配置为 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>3.4.2</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/org.springframework.boot.autoconfigure.AutoConfiguration.imports</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports</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 插件来生成瘦 jar。
<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>
您可以找到整个示例pom.xml
用于部署 Spring Cloud Function 的文件
使用 Maven 的应用程序到 AWS Lambda 此处。
Gradle
要使用 Gradle 的适配器插件,请将依赖项添加到build.gradle
文件:
dependencies {
compile("org.springframework.cloud:spring-cloud-function-adapter-aws:${version}")
}
正如 JAR 布局注释中所指出的,您需要一个着色的 jar 才能上传它 到 AWS Lambda。您可以使用 Gradle Shadow 插件:
您可以使用 Spring Boot Gradle 插件和 Spring Boot Thin Gradle 插件来生成 薄罐子。
下面是一个完整的 gradle 文件
plugins {
id 'java'
id 'org.springframework.boot' version '3.4.2'
id 'io.spring.dependency-management' version '1.1.3'
id 'com.github.johnrengelman.shadow' version '8.1.1'
id 'maven-publish'
id 'org.springframework.boot.experimental.thin-launcher' version "1.0.31.RELEASE"
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
repositories {
mavenCentral()
mavenLocal()
maven { url 'https://repo.spring.io/milestone' }
}
ext {
set('springCloudVersion', "2024.0.0")
}
assemble.dependsOn = [thinJar, shadowJar]
publishing {
publications {
maven(MavenPublication) {
from components.java
versionMapping {
usage('java-api') {
fromResolutionOf('runtimeClasspath')
}
usage('java-runtime') {
fromResolutionResult()
}
}
}
}
}
shadowJar.mustRunAfter thinJar
import com.github.jengelman.gradle.plugins.shadow.transformers.*
shadowJar {
archiveClassifier = 'aws'
manifest {
inheritFrom(project.tasks.thinJar.manifest)
}
// Required for Spring
mergeServiceFiles()
append 'META-INF/spring.handlers'
append 'META-INF/spring.schemas'
append 'META-INF/spring.tooling'
append 'META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports'
append 'META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports'
transform(PropertiesFileTransformer) {
paths = ['META-INF/spring.factories']
mergeStrategy = "append"
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.cloud:spring-cloud-function-adapter-aws'
implementation 'org.springframework.cloud:spring-cloud-function-context'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
tasks.named('test') {
useJUnitPlatform()
}
您可以找到整个示例build.gradle
用于部署 Spring Cloud Function 的文件
使用 Gradle 向 AWS Lambda 的应用程序,请点击此处。
适用于 Spring Boot Web 的无服务器 Java 容器
您可以使用 aws-serverless-java-container 库在 AWS Lambda 中运行 Spring Boot 3 应用程序。这非常适合将现有 Spring 应用程序迁移到 AWS Lambda,或者如果您构建具有多个 API 端点的复杂 API 并希望维护熟悉的RestController
方法。以下部分提供了该过程的高级概述。有关更多信息,请参阅官方示例代码。
-
将无服务器 Java 容器库导入到现有的 Spring Boot 3 Web 应用程序
<dependency> <groupId>com.amazonaws.serverless</groupId> <artifactId>aws-serverless-java-container-springboot3</artifactId> <version>2.1.2</version> </dependency>
-
使用用作入口点的内置 Lambda 函数处理程序
com.amazonaws.serverless.proxy.spring.SpringDelegatingLambdaContainerHandler
-
配置名为
MAIN_CLASS
让泛型处理程序知道在哪里可以找到原始应用程序主类。通常,这是用@SpringBootApplication注释的类。
MAIN_CLAS = com.my.package.MySpringBootApplication
下面可以看到一个示例部署配置:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
MySpringBootLambdaFunction:
Type: AWS::Serverless::Function
Properties:
Handler: com.amazonaws.serverless.proxy.spring.SpringDelegatingLambdaContainerHandler
Runtime: java21
MemorySize: 1024
CodeUri: target/lambda-spring-boot-app-0.0.1-SNAPSHOT.jar #Must be a shaded Jar
Environment:
Variables:
MAIN_CLASS: com.amazonaws.serverless.sample.springboot3.Application #Class annotated with @SpringBootApplication
请在此处找到包括 GraalVM 本机映像在内的所有示例。