AWS Lambda
AWS 适配器采用 Spring Cloud Function 应用,并将其转换为可以在 AWS Lambda 中运行的形式。
The AWS adapter takes a Spring Cloud Function app and converts it to a form that can run in AWS Lambda.
如何开始使用 AWS Lambda 的详细信息不在本文档的范围内,因此预期用户已对 AWS 和 AWS Lambda 有些了解,并希望了解 spring 提供的附加价值。
The details of how to get stared with AWS Lambda is out of scope of this document, so the expectation is that user has some familiarity with AWS and AWS Lambda and wants to learn what additional value spring provides.
Getting Started
Spring Cloud 函数框架的一个目标是提供必要的元素以使一个 简单的函数应用程序 在特定环境中以特定方式进行交互。一个简单的函数应用程序(在内容或 Spring 中)是一个包含 Supplier、Function 或 Consumer 类型的 bean 的应用程序。因此,使用 AWS 意味着某个简单的函数 bean 应在 AWS Lambda 环境中得到某种认可并执行。
One of the goals of Spring Cloud Function framework is to provide necessary infrastructure elements to enable a simple function application to interact in a certain way in a particular environment. A simple function application (in context or Spring) is an application that contains beans of type Supplier, Function or Consumer. So, with AWS it means that a simple function bean should somehow be recognised and executed in AWS Lambda environment.
我们来看看这个示例:
Let’s look at the example:
@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 应用程序。不需要其他代码或配置。您需要做的就是打包并部署它,让我们看看我们如何做到这一点。
It shows a complete Spring Boot application with a function bean defined in it. What’s interesting is that on the surface this is just another boot app, but in the context of AWS Adapter it is also a perfectly valid AWS Lambda application. No other code or configuration is required. All you need to do is package it and deploy it, so let’s look how we can do that.
为了简化操作,我们提供了一个准备构建和部署的示例项目,您可以在 here 中访问它。
To make things simpler we’ve provided a sample project ready to be built and deployed and you can access it here.
您只需执行 ./mvnw clean package
即可生成 JAR 文件。所有必需的 Maven 插件已设置好,以生成可部署到 AWS 的适当 JAR 文件。(您可以在 Notes on JAR Layout 中阅读更多有关 JAR 布局的详细信息)。
You simply execute ./mvnw clean package
to generate JAR file. All the necessary maven plugins have already been setup to generate
appropriate AWS deployable JAR file. (You can read more details about JAR layout in Notes on JAR Layout).
然后,您必须将 JAR 文件(通过 AWS 信息面板或 AWS CLI)上传到 AWS。
Then you have to upload the JAR file (via AWS dashboard or AWS CLI) to AWS.
当被问及 处理程序 时,您指定 org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest
,它是一个通用请求处理程序。
When ask about handler you specify org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest
which is a generic request handler.
就这样。使用一些示例数据保存并执行函数,此函数期望为一个字符串,该函数会将其大写,然后返回。
That is all. Save and execute the function with some sample data which for this function is expected to be a String which function will uppercase and return back.
虽然 org.springframework.cloud.function.adapter.aws.FunctionInvoker
是一个通用的 AWS RequestHandler
实现,旨在完全让您与 AWS Lambda API 的具体细节隔离开来,但在某些情况下,您可能需要指定要使用的具体 AWS RequestHandler
。下一部分将向您解释如何实现这一操作。
While org.springframework.cloud.function.adapter.aws.FunctionInvoker
is a general purpose AWS’s RequestHandler
implementation aimed at completely
isolating you from the specifics of AWS Lambda API, for some cases you may want to specify which specific AWS’s RequestHandler
you want
to use. The next section will explain you how you can accomplish just that.
AWS Request Handlers
虽然 AWS Lambda 允许您实现各种 RequestHandlers
,但使用 Spring Cloud Function 您无需实现任何 RequestHandler
,而是可以使用提供的 org.springframework.cloud.function.adapter.aws.FunctionInvoker
,它是 AWS 的 RequestStreamHandler
的实现。用户只需要在部署函数时在 AWS 仪表板上将其指定为“处理程序”,无需做其他任何事情。它将处理大多数情况,包括 Kinesis、流等。
While AWS Lambda allows you to implement various RequestHandlers
, with Spring Cloud Function you don’t need to implement any, and instead use the provided
org.springframework.cloud.function.adapter.aws.FunctionInvoker
which is the implementation of AWS’s RequestStreamHandler
.
User doesn’t need to do anything other then specify it as 'handler' on AWS dashboard when deploying function.
It will handle most of the case including Kinesis, streaming etc. .
如果您的应用程序包含多个 @Bean
,其类型为 Function
等,则可以通过配置 spring.cloud.function.definition
属性或环境变量来选择一个 Function
。这些函数从 Spring Cloud FunctionCatalog
中提取。如果您未指定 spring.cloud.function.definition
,框架将尝试按照以下搜索顺序查找一个默认值:首先搜索 Function
,然后搜索 Consumer
,最后搜索 Supplier
)。
If your app has more than one @Bean
of type Function
etc. then you can choose the one to use by configuring spring.cloud.function.definition
property or environment variable. The functions are extracted from the Spring Cloud FunctionCatalog
. In the event you don’t specify spring.cloud.function.definition
the framework will attempt to find a default following the search order where it searches first for Function
then Consumer
and finally Supplier
).
Type Conversion
Spring Cloud Function 将尝试在原始输入流和函数声明的类型之间透明地处理类型转换。
Spring Cloud Function will attempt to transparently handle type conversion between the raw input stream and types declared by your function.
例如,如果您的函数签名如下 Function<Foo, Bar>
,我们将尝试将传入流事件转换为 Foo
的实例。
For example, if your function signature is as such Function<Foo, Bar>
we will attempt to convert
incoming stream event to an instance of Foo
.
如果事件类型未知或无法确定(例如,Function<?, ?>
),我们将尝试将传入流事件转换为泛型 Map
。
In the event type is not known or can not be determined (e.g., Function<?, ?>
) we will attempt to
convert an incoming stream event to a generic Map
.
Raw Input
有时,您可能希望访问原始输入。在这种情况下,您所需要做的就是声明您的函数签名以接受 InputStream
。例如,Function<InputStream, ?>
。在这种情况下,我们不会尝试任何转换,并将原始输入直接传递给函数。
There are times when you may want to have access to a raw input. In this case all you need is to declare your
function signature to accept InputStream
. For example, Function<InputStream, ?>
. In this case
we will not attempt any conversion and will pass the raw input directly to a function.
AWS Function Routing
Spring Cloud Function 的核心特性之一是 routing,它可以根据用户提供的路由指令将一个特殊函数委派给其他函数。
One of the core features of Spring Cloud Function is routing - an ability to have one special function to delegate to other functions based on the user provided routing instructions.
在 AWS Lambda 环境中,此功能提供了一项附加好处,因为它允许您将单个函数(路由函数)作为 AWS Lambda 和一个 API 网关的单一 HTTP 端点进行绑定。因此,在最后,您只需管理一个函数和一个端点,同时还可从可作为应用程序一部分的众多函数中受益。
In AWS Lambda environment this feature provides one additional benefit, as it allows you to bind a single function (Routing Function) as AWS Lambda and thus a single HTTP endpoint for API Gateway. So in the end you only manage one function and one endpoint, while benefiting from many function that can be part of your application.
更多详细信息可在提供的 sample 中找到,然而,有一些值得一提的一般性内容。
More details are available in the provided sample, yet few general things worth mentioning.
每当应用程序中存在多于一个函数时,路由功能都会默认启用,因为 org.springframework.cloud.function.adapter.aws.FunctionInvoker
无法确定哪个函数绑定为 AWS Lambda,因此将默认设置为 RoutingFunction
。这意味着您只需提供路由指令,您可以使用以下几种机制执行此操作:https://docs.spring.io/spring-cloud-function/docs/{project-version}/reference/html/spring-cloud-function.html#_function_routing_and_filtering[(有关更多详情,请参见样本:https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-aws-routing)。
Routing capabilities will be enabled by default whenever there is more then one function in your application as org.springframework.cloud.function.adapter.aws.FunctionInvoker
can not determine which function to bind as AWS Lambda, so it defaults to RoutingFunction
.
This means that all you need to do is provide routing instructions which you can do using several mechanisms
(see sample for more details).
此外,请注意,由于 AWS 不允许在环境变量的名称中使用句点“.”和/或连字符“—”,因此您可以受益于启动支持,简单地将句点替换为下划线,并将连字符替换为驼峰式。因此,例如 spring.cloud.function.definition
变为 spring_cloud_function_definition
,而 spring.cloud.function.routing-expression
变为 spring_cloud_function_routingExpression
。
Also, note that since AWS does not allow dots .
and/or hyphens`-` in the name of the environment variable, you can benefit from boot support and simply substitute
dots with underscores and hyphens with camel case. So for example spring.cloud.function.definition
becomes spring_cloud_function_definition
and spring.cloud.function.routing-expression
becomes spring_cloud_function_routingExpression
.
Custom Runtime
您还可以受益于 AWS Lambda 的 AWS Lambda 自定义运行时功能,Spring Cloud Function 提供所有必要的组件,便于轻松使用。
You can also benefit from AWS Lambda custom runtime feature of AWS Lambda and Spring Cloud Function provides all the necessary components to make it easy.
从代码角度来看,应用程序看起来应该与任何其他 Spring Cloud Function 应用程序没有区别。您需要做的唯一事情是在 zip/jar 的根目录中提供一个“bootstrap”脚本,用于运行 Spring Boot 应用程序,并在 AWS 中创建函数时选择“自定义运行时”。以下是一个示例“bootstrap”文件:
From the code perspective the application should look no different then any other Spring Cloud Function application.
The only thing you need to do is to provide a bootstrap
script in the root of your zip/jar that runs the Spring Boot application.
and select "Custom Runtime" when creating a function in AWS.
Here is an example 'bootstrap' file:
#!/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 的应用程序。
The com.example.LambdaApplication
represents your application which contains function beans.
在 AWS 中将处理程序名称设置为函数名称。您还可以在此处使用函数组合(例如,uppecrase|reverse
)。这几乎就是全部了。一旦您将 zip/jar 上传到 AWS,函数将在自定义运行时中运行。我们提供了一个示例项目, 您还可以在其中看到如何正确配置 POM 以生成 zip 文件:https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-aws-custom-new
Set the handler name in AWS to the name of your function. You can use function composition here as well (e.g., uppecrase|reverse
).
That is pretty much all. Once you upload your zip/jar to AWS your function will run in custom runtime.
We provide a sample project
where you can also see how to configure yoru POM to properly generate the zip file.
功能 bean 定义样式也适用于自定义运行时,并且比 @Bean
样式更快。自定义运行时甚至可以比 Java lambda 的功能 bean 实现启动得更快 - 它主要取决于您在运行时需要加载的类数量。Spring 在此处做得并不多,因此您可以通过仅在函数中使用基元类型(例如,不执行任何自定义 @PostConstruct
初始化程序的工作)来减少冷启动时间。
The functional bean definition style works for custom runtimes as well, and is
faster than the @Bean
style. A custom runtime can start up much quicker even than a functional bean implementation
of a Java lambda - it depends mostly on the number of classes you need to load at runtime.
Spring doesn’t do very much here, so you can reduce the cold start time by only using primitive types in your function, for instance,
and not doing any work in custom @PostConstruct
initializers.
AWS Function Routing with Custom Runtime
使用 Custom Runtime 时,函数路由工作方式相同。您所需要做的就是将 functionRouter
指定为 AWS 处理程序,这种方式与您将函数名称用作处理程序的方式相同。
When using Custom Runtime Function Routing works the same way. All you need is to specify functionRouter
as AWS Handler the same way you would use the name of the function as handler.
Deploying Container images
自定义运行时还负责处理容器映像部署。当以与 here中所述类似的方式部署容器映像时,请务必使用函数名称设置和环境变量 DEFAULT_HANDLER
。
Custom Runtime is also responsible for handling of container image deployments.
When deploying container images in a way similar to the one described here, it is important
to remember to set and environment variable DEFAULT_HANDLER
with the name of the function.
例如,对于下面所示的函数 Bean,DEFAULT_HANDLER
值应为 readMessageFromSQS
。
For example, for function bean shown below the DEFAULT_HANDLER
value would be readMessageFromSQS
.
@Bean
public Consumer<Message<SQSMessageEvent>> readMessageFromSQS() {
return incomingMessage -> {..}
}
此外,务必要记住确保 spring_cloud_function_web_export_enabled
也设置为 false
。默认情况下,它就是 false
。
Also, it is important to remember to ensure tht spring_cloud_function_web_export_enabled
is also set to false
. It is by default.
Notes on JAR Layout
您在 Lambda 中的运行时不需要 Spring Cloud Function Web 或 Stream 适配器,因此您可能需要在将 JAR 发送到 AWS 之前将它们排除在外。Lambda 应用程序必须进行 shaded,但是 Spring Boot 独立应用程序无需进行 shaded,因此您可以使用 2 个单独的 jar 运行相同的应用程序(根据示例)。示例应用程序创建 2 个 jar 文件,一个带有 aws
分类器,用于在 Lambda 中部署,一个带有 [id="thin-jar"][id="thin-jar"] 的可执行(精简)jar 文件,其中在运行时 spring-cloud-function-web
。Spring Cloud Function 会尝试从 JAR 文件清单中针对您找出“主类”,它使用 Start-Class
属性(如果您使用启动器父,则 Spring Boot 工具将为您添加该属性)。如果清单中没有 Start-Class
,当您将该功能部署到 AWS 时,您可以使用环境变量或系统属性 MAIN_CLASS
。
You don’t need the Spring Cloud Function Web or Stream adapter at runtime in Lambda, so you might
need to exclude those before you create the JAR you send to AWS. A Lambda application has to be
shaded, but a Spring Boot standalone application does not, so you can run the same app using 2
separate jars (as per the sample). The sample app creates 2 jar files, one with an aws
classifier for deploying in Lambda, and one [id="thin-jar"] executable (thin) jar that includes spring-cloud-function-web
at runtime. Spring Cloud Function will try and locate a "main class" for you from the JAR file
manifest, using the Start-Class
attribute (which will be added for you by the Spring Boot
tooling if you use the starter parent). If there is no Start-Class
in your manifest you can
use an environment variable or system property MAIN_CLASS
when you deploy the function to AWS.
如果您不使用函数 bean 定义而是依赖于 Spring Boot 的自动配置,并且不依赖于 spring-boot-starter-parent
,则必须在 maven-shade-plugin 执行中配置其他转换器。
If you are not using the functional bean definitions but relying on Spring Boot’s auto-configuration,
and are not depending on spring-boot-starter-parent
,
then additional transformers must be configured as part of the maven-shade-plugin execution.
<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.4</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>
Build file setup
为了在 AWS Lambda 上运行 Spring Cloud Function 应用程序,可以利用云平台提供商提供的 Maven 或 Gradle 插件。
In order to run Spring Cloud Function applications on AWS Lambda, you can leverage Maven or Gradle plugins offered by the cloud platform provider.
Maven
要使用适用于 Maven 的适配器插件,请将插件依赖项添加到您的 pom.xml
文件中:
In order to use the adapter plugin for Maven, add the plugin dependency to your pom.xml
file:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-aws</artifactId>
</dependency>
</dependencies>
正如在Notes on JAR Layout中指出的,你将需要一个经过着色的jar文件以便将其上传到AWS Lambda。你可以为此使用 Maven Shade Plugin。setup的示例可以在上方找到。
As pointed out in the Notes on JAR Layout, you will need a shaded jar in order to upload it to AWS Lambda. You can use the Maven Shade Plugin for that. The example of the setup can be found above.
您可以使用 Spring Boot Maven 插件生成 thin jar。
You can use the Spring Boot Maven Plugin to generate the thin 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>
你可以找到用于将 Spring Cloud Function 应用程序部署到 AWS Lambda(使用 Maven here)的完整示例 pom.xml
文件。
You can find the entire sample pom.xml
file for deploying Spring Cloud Function
applications to AWS Lambda with Maven here.
Gradle
要使用适用于 Gradle 的适配器插件,请将依赖项添加到您的 build.gradle
文件:
In order to use the adapter plugin for Gradle, add the dependency to your build.gradle
file:
dependencies {
compile("org.springframework.cloud:spring-cloud-function-adapter-aws:${version}")
}
正如在Notes on JAR Layout中指出的,你将需要一个经过着色的jar文件以便将其上传到AWS Lambda。你可以为此使用 Gradle Shadow Plugin:
As pointed out in Notes on JAR Layout, you will need a shaded jar in order to upload it to AWS Lambda. You can use the Gradle Shadow Plugin for that:
您可以使用 Spring Boot Gradle 插件和 Spring Boot Thin Gradle 插件生成 thin jar。
You can use the Spring Boot Gradle Plugin and Spring Boot Thin Gradle Plugin to generate the thin jar.
下面是一个完整的 gradle 文件
Below is a complete gradle file
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.0-M2'
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', "2023.0.0-M1")
}
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()
}
Gradle 部署 Spring Cloud Function 应用至 AWS Lamda 的完整示例 build.gradle
文件,请访问 here。
You can find the entire sample build.gradle
file for deploying Spring Cloud Function
applications to AWS Lambda with Gradle here.