The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

Amazon Lambda

quarkus-amazon-lambda 扩展允许你使用Quarkus来构建你的AWS Lambdas。你的Lambdas可以根据需要使用来自CDI或Spring的注入注解,以及其他Quarkus设施。

Quarkus lambdas可以使用Amazon Java Runtime进行部署,如果你想要更小的内存占用和更快的冷启动启动时间,你也可以构建一个本地可执行文件,并使用Amazon的Custom Runtime。

Quarkus’s integration with lambdas also supports Quarkus’s Live Coding development cycle. You can bring up your Quarkus lambda project in dev or test mode and code on your project live.

先决条件

要完成这个指南,你需要:

  • 大概30 minutes

  • 编辑器

  • 安装JDK 11以上版本并正确配置了 JAVA_HOME

  • Apache Maven 3.8.1+

  • 如果你愿意的话,还可以选择使用Quarkus CLI

  • 如果你想构建原生可执行程序,可以选择安装Mandrel或者GraalVM,并正确配置(或者使用Docker在容器中进行构建)

  • 亚马逊AWS账户

  • AWS CLI

  • AWS SAM CLI ,用于本地测试

对于Gradle项目,请 见下文 ,或进一步参考 Gradle设置页面中 的指南。

入门

本指南将指导你通过一个maven原型生成一个Java项目的例子,并将其部署到AWS。

安装AWS位

安装所有AWS位可能是本指南中最困难的事情。请确保你遵循安装AWS CLI的所有步骤。

创建Maven部署项目

使用我们的Maven Archetype创建Quarkus AWS Lambda maven项目。

mvn archetype:generate \
       -DarchetypeGroupId=io.quarkus \
       -DarchetypeArtifactId=quarkus-amazon-lambda-archetype \
       -DarchetypeVersion=2.11.2.Final

如果你喜欢使用Gradle,你可以通过 code.quarkus.ioquarkus-amazon-lambda 扩展添加为依赖项,来快速而轻松地生成一个Gradle项目。

将build.gradle、gradle.properties和settings.gradle复制到上述生成的Maven archetype项目中,以按照本指南进行操作。

Execute: gradle wrapper to set up the gradle wrapper (recommended).

关于Gradle的全部细节,请见下文

选择你的Lambda

quarkus-amazon-lambda 扩展会为直接实现亚马逊 RequestHandler<?, ?>RequestStreamHandler 接口的类扫描你的项目。它必须在你的项目中找到一个实现该接口的类,否则它将抛出一个构建时间失败。如果它发现一个以上的处理程序类,也会抛出一个构建时间异常。

但有时,你可能会有几个共享代码的相关lambdas,创建多个maven模块是你不想做的开销。 quarkus-amazon-lambda 扩展允许你在一个项目中捆绑多个lambdas,并使用配置或环境变量来选择你要部署的处理程序。

生成的项目里有三个lambdas。两个实现 RequestHandler<?, ?> 接口,一个实现 RequestStreamHandler 接口。使用了一个,另两个未使用。如果你打开 src/main/resources/application.properties ,你会看到:

quarkus.lambda.handler=test

quarkus.lambda.handler 属性告诉Quarkus要部署哪个lambda处理程序。这也可以用一个环境变量来覆盖。

如果你查看项目中生成的三个处理程序类,你会发现它们的 @Named 不同。

@Named("test")
public class TestLambda implements RequestHandler<InputObject, OutputObject> {
}

@Named("unused")
public class UnusedLambda implements RequestHandler<InputObject, OutputObject> {
}

@Named("stream")
public class StreamLambda implements RequestStreamHandler {
}

处理程序类的CDI名称必须与 quarkus.lambda.handler 属性中指定的值匹配。

部署到AWS Lambda Java Runtime

有几个步骤可以让lambda在AWS上运行。生成的maven项目包含一个有用的脚本,来为纯Java和原生部署创建、更新、删除和调用你的lambda。

构建和部署

构建项目。

CLI
quarkus build
Maven
./mvnw clean package
Gradle
./gradlew build

这将对你的代码进行编译和打包。

创建一个执行角色

查看使用AWS CLI部署lambda的 入门指南 。具体来说,确保你已经创建了一个 Execution Role 。你需要在你的配置文件或控制台窗口中定义一个 LAMBDA_ROLE_ARN 环境变量,或者,你可以编辑由构建生成的 manage.sh 脚本,并把角色值直接放在那里:

LAMBDA_ROLE_ARN="arn:aws:iam::1234567890:role/lambda-role"

构建额外生成的文件

After you run the build, there are a few extra files generated by the quarkus-amazon-lambda extension. These files are in the build directory: target/ for maven, build/ for gradle.

  • function.zip - lambda部署文件

  • manage.sh - aws lambda cli调用的包装器

  • bootstrap-example.sh - 用于原生部署的bootstrap脚本示例

  • sam.jvm.yaml - (可选)用于sam cli和本地测试

  • sam.native.yaml - (可选),用于Sam cli和原生本地测试

创建函数

target/manage.sh 脚本是用于使用AWS Lambda Java runtime管理你的lambda。此脚本只是为了方便你而提供。如果你想了解执行哪些aws命令来创建、删除和更新你的lambdas,请检查 manage.sh 脚本的输出。

manage.sh 支持四种操作: createdeleteupdateinvoke

To verify your setup, that you have the AWS CLI installed, executed aws configure for the AWS access keys, and set up the LAMBDA_ROLE_ARN environment variable (as described above), please execute manage.sh without any parameters. A usage statement will be printed to guide you accordingly.
如果使用Gradle, manage.sh 中二进制文件的路径必须从 target 变为 build

要查看 用法 声明,并验证AWS配置:

sh target/manage.sh

你可以使用以下命令 创建 你的函数:

sh target/manage.sh create

或者如果在这个shell中没有定义 LAMBDA_ROLE_ARN

LAMBDA_ROLE_ARN="arn:aws:iam::1234567890:role/lambda-role" sh target/manage.sh create
不要改变处理程序的开关。这必须被硬编码为 io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest 。这个处理程序引导Quarkus,并包装你实际的处理程序,以便可以执行注入。

如果在创建函数时有任何问题,你必须在重新运行 create 命令之前使用 delete 函数来删除它。

sh target/manage.sh delete

命令也可以是叠加的:

sh target/manage.sh delete create

调用Lambda

使用 invoke 命令来调用你的函数。

sh target/manage.sh invoke

这个lambda示例接受通过 --payload 开关传入的输入,该开关指向项目根目录中的一个json文件。

这个lambda也可以使用SAM CLI在本地调用,如:

sam local invoke --template target/sam.jvm.yaml --event payload.json

如果你正在使用原生镜像构建,只需将模板名称替换为原生版本:

sam local invoke --template target/sam.native.yaml --event payload.json

更新Lambda

你可以在你认为合适的时候更新Java代码。一旦你重建了,你可以通过执行 update 命令来重新部署你的lambda。

sh target/manage.sh update

部署到AWS Lambda Custom (native) Runtime

如果你想让你的lambda有更低的内存占用和更快的初始化时间,你可以把你的Java代码编译成本地可执行文件。只是确保要用 -Pnative 开关重建你的项目。

对于Linux主机,执行。

CLI
quarkus build --native
Maven
./mvnw package -Dnative
Gradle
./gradlew build -Dquarkus.package.type=native
如果你在非Linux系统上构建,你还需要传入一个属性,指示Quarkus使用docker构建,因为Amazon Lambda需要linux二进制文件。你可以通过向你的构建传递这个属性来实现: -Dquarkus.native.container-build=true 。不过,这需要你在本地安装了Docker。
CLI
quarkus build --native --no-tests -Dquarkus.native.container-build=true
# The --no-tests flag is required only on Windows and macOS.
Maven
./mvnw package -Dnative -Dquarkus.native.container-build=true
Gradle
./gradlew build -Dquarkus.package.type=native -Dquarkus.native.container-build=true

这两个命令中的任何一个都会编译并创建一个本地可执行镜像。它还会生成一个zip文件 target/function.zip 。这个zip文件包含已重命名为 bootstrap 的本地可执行镜像,。这是AWS Lambda Custom (Provided) Runtime的要求。

这里的说明与上面的完全一样,但有一个变化:你需要将 native 作为第一个参数添加到 manage.sh 脚本中:

sh target/manage.sh native create

如上所述,命令可以叠加。如果你想使用原生镜像构建,唯一的要求是 native 是第一个参数。该脚本将处理管理你原生镜像功能部署所需的其余细节。

如果你想了解执行了哪些aws命令来创建、删除和更新lambdas,请检查 manage.sh 脚本的输出。

对于原生的创建命令,需要注意的一点是, aws lambda create-function 调用必须设置一个特定的环境变量:

--environment 'Variables={DISABLE_SIGNAL_HANDLERS=true}'

检查POM和Gradle构建

除了将 quarkus-amazon-lambda 扩展作为一个依赖项之外,POM并没有什么特别之处。该扩展会为你的lambda部署自动生成可能需要的一切。

在这个扩展的前几个版本中,你必须设置你的pom或gradle来为原生部署压缩你的可执行文件,但现在不再是这样了。

Gradle构建

Similarly, for Gradle projects, you also just have to add the quarkus-amazon-lambda dependency. The extension automatically generates everything you might need for your lambda deployment.

Gradle依赖项示例:

dependencies {
    implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")
    implementation 'io.quarkus:quarkus-resteasy'
    implementation 'io.quarkus:quarkus-amazon-lambda'

    testImplementation 'io.quarkus:quarkus-junit5'
    testImplementation 'io.rest-assured:rest-assured'
}

实时编码和单元/集成测试

为了在开发环境中尽可能地反映AWS Lambda环境,Quarkus Amazon Lambda扩展在Quarkus 开发和测试模式下启动了一个模拟的AWS Lambda事件服务器。这个模拟的事件服务器模拟了一个真正的AWS Lambda环境。

当在Quarkus开发模式下运行时,你可以对 <a href="http://localhost:8080" class="bare">http://localhost:8080</a>; 执行HTTP POST来向它提供事件。模拟事件服务器将接收事件,你的lambda将被调用。你可以在你的lambda上执行实时编码,所做的修改将自动被重新编译,并在下次调用时可用。下面是一个例子:

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev
$ curl -d "{\"name\":\"John\"}" -X POST http://localhost:8080

For your unit tests, you can also invoke on the mock event server using any HTTP client you want. Here’s an example using rest-assured. Quarkus starts up a separate Mock Event server under port 8081. The default port for Rest Assured is automatically set to 8081 by Quarkus, so you can invoke on this endpoint.

import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.containsString;

@QuarkusTest
public class LambdaHandlerTest {

    @Test
    public void testSimpleLambdaSuccess() throws Exception {
        Person in = new Person();
        in.setName("Stu");
        given()
                .contentType("application/json")
                .accept("application/json")
                .body(in)
                .when()
                .post()
                .then()
                .statusCode(200)
                .body(containsString("Hello Stu"));
    }
}

也会为 @NativeImageTest@QuarkusIntegrationTest 测试启动模拟事件服务器,因此其也会与本地二进制文件一起工作。所有这些都为SAM CLI本地测试提供了类似的功能,而没有Docker的开销。

最后,如果端口8080或端口8081在你的电脑上不可用,你可以使用application.properties修改开发和测试模式的端口

quarkus.lambda.mock-event-server.dev-port=8082
quarkus.lambda.mock-event-server.test-port=8083

端口值为零将导致随机分配端口。

使用SAM CLI进行测试

如果你不想使用模拟事件服务器,你可以用SAM CLI测试lambdas。

AWS SAM CLI 允许你在模拟的Lambda环境中在你的笔记本电脑上本地运行lambdas。这需要安装 docker 。如果你选择使用它,这是一个可选的方法。否则,Quarkus的JUnit集成应该足以满足你的大部分需求。

已经为JVM和原生执行模式生成了一个启动模板。

运行下面的SAM CLI命令来本地测试你的lambda函数,传递合适的SAM 模板event 参数使用任何JSON文件,在本例中是样本 payload.json

如果使用Gradle,YAML模板中的二进制文件的路径必须从 target 改为 build
sam local invoke --template target/sam.jvm.yaml --event payload.json

本地镜像也可以使用 sam.native.yaml 模板进行本地测试:

sam local invoke --template target/sam.native.yaml --event payload.json

修改 function.zip

There are times when you may have to add some additions to the function.zip lambda deployment that is generated by the build. To do this, create a zip.jvm or zip.native directory within src/main. Create zip.jvm/ if you are doing a pure Java lambda. zip.native/ if you are doing a native deployment.

你在zip目录下创建的任何文件和目录都将包括在 function.zip

自定义 bootstrap 脚本

有些时候你可能想在lambda调用你的原生quarkus lambda部署时设置特定的系统属性或其他参数。如果你在 zip.native 中包含一个 bootstrap 脚本文件,quarkus 扩展会在 function.zip 中自动将可执行文件重命名为 runner ,并将 bootstrap 脚本的 unix 模式设置为可执行。

如果你包含一个自定义的 bootstrap 脚本,那么本地可执行文件必须被引用为 runner

扩展会在 target/bootstrap-example.sh 中生成了一个示例脚本。

使用AWS XRay和GraalVM进行追踪

If you are building native images, and want to use AWS X-Ray Tracing with your lambda you will need to include quarkus-amazon-lambda-xray as a dependency in your pom. The AWS X-Ray library is not fully compatible with GraalVM, so we had to do some integration work to make this work.

此外,记得在 manage.shcmd_create() 函数中启用AWS X-Ray追踪参数。这也可以在AWS管理控制台中进行设置。

    --tracing-config Mode=Active

对于Sam模板文件,将以下内容添加到YAML函数属性中。

    Tracing: Active

AWS X-Ray确实在你的发行版中添加了许多类,确保你至少使用了256MB的AWS Lambda内存大小。这在 manage.sh cmd_create() 中进行了明确的设置。虽然原生镜像可能总是使用较低的内存设置,但建议保持设置不变,特别是为了帮助比较性能。

使用HTTPS或SSL/TLS

If your code makes HTTPS calls (e.g. to a microservice, to an AWS service), you will need to add configuration to the native image, as GraalVM will only include the dependencies when explicitly declared. Quarkus, by default enables this functionality on extensions that implicitly require it. For further information, please consult the Quarkus SSL guide

打开src/main/resources/application.properties,并在你的原生镜像中添加以下行来启用SSL。

quarkus.ssl.native=true

使用AWS Java SDK v2

Quarkus现在有DynamoDB、S3、SNS和SQS的扩展(还会有更多)。请查看 那些指南 关于如何使用带有Quarkus的各种AWS服务 ,而不是像下面这样手动连接。

通过最小的整合,可以利用AWS Java SDK v2,其可以用来调用诸如SQS、SNS、S3和DynamoDB等服务。

然而,对于原生镜像,由于GraalVM编译中的问题(目前),在使用同步模式时,URL Connection客户端必须优先于Apache HTTP客户端。

quarkus-jaxb 作为一个依赖项添加到你的Maven pom.xml ,或Gradle build.gradle 文件中。

You must also force your AWS service client for SQS, SNS, S3 et al., to use the URL Connection client, which connects to AWS services over HTTPS, hence the inclusion of the SSL enabled property, as described in the 使用HTTPS或SSL/TLS section above.

// select the appropriate client, in this case SQS, and
// insert your region, instead of XXXX, which also improves startup time over the default client
  client = SqsClient.builder().region(Region.XXXX).httpClient(software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient.builder().build()).build();

对于Maven,在你的 pom.xml 中添加以下内容。

    <properties>
        <aws.sdk2.version>2.10.69</aws.sdk2.version>
    </properties>

    <dependencyManagement>
        <dependencies>

            <dependency>
                <groupId>software.amazon.awssdk</groupId>
                <artifactId>bom</artifactId>
                <version>${aws.sdk2.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

        </dependencies>
    </dependencyManagement>
    <dependencies>

        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>url-connection-client</artifactId>
        </dependency>

        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>apache-client</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <!-- sqs/sns/s3 etc -->
            <artifactId>sqs</artifactId>
            <exclusions>
                <!-- exclude the apache-client and netty client -->
                <exclusion>
                    <groupId>software.amazon.awssdk</groupId>
                    <artifactId>apache-client</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>software.amazon.awssdk</groupId>
                    <artifactId>netty-nio-client</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.jboss.logging</groupId>
            <artifactId>commons-logging-jboss-logging</artifactId>
        </dependency>
    </dependencies>
如果你看到 java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty 或类似的SSL错误,由于GraalVM的当前状态,有一些额外的工作来捆绑 function.zip ,如下所示。更多信息,请参见 Quarkus Native SSL指南

对客户端SSL的额外要求

本地可执行文件需要一些额外的步骤来启用S3和其他AWS库需要的客户端SSL。

  1. 一个自定义的 bootstrap 脚本

  2. 必须将 libsunec.so 添加到 function.zip

  3. 必须将 cacerts 添加到 function.zip

To do this, first create a directory src/main/zip.native/ with your build. Next create a shell script file called bootstrap within src/main/zip.native/, like below. An example is created automatically in your build folder (target or build), called bootstrap-example.sh

#!/usr/bin/env bash

./runner -Djava.library.path=./ -Djavax.net.ssl.trustStore=./cacerts

如果你的 cacerts 文件有密码保护,则额外设置 -Djavax.net.ssl.trustStorePassword=changeit

接下来,你必须将GraalVM发行版中的一些文件复制到 src/main/zip.native/

GraalVM versions can have different paths for these files whether you are using the Java 8 or 11 version. Adjust accordingly.
cp $GRAALVM_HOME/lib/libsunec.so $PROJECT_DIR/src/main/zip.native/
cp $GRAALVM_HOME/lib/security/cacerts $PROJECT_DIR/src/main/zip.native/

现在,当你运行原生构建时,所有这些文件都将包含在 function.zip

如果你使用Docker镜像来进行构建,那么你必须从这个镜像中提取这些文件。

要提取所需的ssl,你必须在后台启动一个Docker容器,并附加到该容器来复制工件。

首先,让我们启动GraalVM容器,注意容器ID的输出。

docker run -it -d --entrypoint bash quay.io/quarkus/ubi-quarkus-native-image:22.1-java11

# This will output a container id, like 6304eea6179522aff69acb38eca90bedfd4b970a5475aa37ccda3585bc2abdde
# Note this value as we will need it for the commands below

First, libsunec.so, the C library used for the SSL implementation:

docker cp {container-id-from-above}:/opt/graalvm/lib/libsunec.so src/main/zip.native/

Second, cacerts, the certificate store. You may need to periodically obtain an updated copy, also.

docker cp {container-id-from-above}:/opt/graalvm/lib/security/cacerts src/main/zip.native/

你的最终档案将看起来像这样:

jar tvf target/function.zip

    bootstrap
    runner
    cacerts
    libsunec.so

使用容器镜像部署到AWS Lambda

AWS Lambda supports creating your lambdas by referencing container images rather than uploading ZIP files. This can have some benefits such as bypassing the size limit of the uploaded ZIP files. You can define lambda functions for both native builds and regular JVM builds.

JVM容器镜像

对于正常的JVM发行版,你需要在官方AWS Java基础镜像之上建立你的镜像。下面是一个Dockerfile的例子,它将从你的Quarkus Lambda项目创建一个容器镜像。它假设已经执行了 mvn package ,并且二进制文件已存在于 target/ 目录中:

FROM  public.ecr.aws/lambda/java:11

ADD target/my-service-0.0.1-SNAPSHOT-runner.jar /var/task/lib/my-service.jar
ADD target/lib/  /var/task/lib/

CMD ["io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest"]

本地可执行的容器镜像

To create a lambda container image that uses the native executable we’ll need to do things a little differently. In this case, we won’t need to use the java:11 base image from AWS, but instead we’ll use a special image that assumes that the runtime environment for the lambda is provided. The example below creates such a container. It assumes that a Maven build has been executed (such as mvn package -Dnative=true) and has generated the native binary into the target/ directory. The binary needs to be named bootstrap and be placed in /var/runtime/:

FROM  public.ecr.aws/lambda/provided

ADD target/my-service-0.0.1-SNAPSHOT-runner /var/runtime/bootstrap
RUN chmod ugo+x /var/runtime/bootstrap

CMD ["io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest"]

部署一个容器镜像lambda

下面,你可以看到如何使用 dockeraws 命令行工具将上面创建的容器镜像构建并部署到AWS中。这些指令对原生和jvm容器镜像都适用,并假设已经登录到 aws 命令行工具。

构建Docker镜像

# Assuming we are located in the root directory of the project and created a Dockerfile there
docker build .
   [output omitted]
    => exporting to image                    0.0s
    => => exporting layers                   0.0s
    => => writing image sha256:[SOME SHA]    0.0s

在用户的AWS账户中创建一个ECR存储库

aws ecr create-repository --repository-name my/test/quarkus-lambda

使用你的ECR注册信息给镜像打上标签

docker tag [SOME SHA] [YOUR AWS ACCOUNT ID].dkr.ecr.[YOUR AWS ACCOUNT REGION].amazonaws.com/my/test/quarkus-lambda:v1

将Docker记录到你的ECR注册表中,并将Docker镜像推送到其中

aws ecr get-login-password --region region | docker login --username AWS --password-stdin [YOUR AWS ACCOUNT ID].dkr.ecr.[YOUR AWS ACCOUNT REGION].amazonaws.com
docker push [YOUR AWS ACCOUNT ID].dkr.ecr.[YOUR AWS ACCOUNT REGION].amazonaws.com/my/test/quarkus-lambda:v1

用AWS CLI工具创建AWS lambda函数

请确保你引用了之前上传的镜像(假设存在一个可以用来运行lambda的角色)。请注意,对于JVM lambda函数来说,默认的 128Mb 内存限制不足以运行该函数,这不是不可能的。在这种情况下,你可以在创建函数时通过向 aws lambda create-function 命令提供 --memory-size 256 参数来增加内存限制。你也可以在创建函数后,在AWS控制台中调整该函数。

aws lambda create-function --function-name my-test-quarkus-lambda-function --package-type Image --code ImageUri=[YOUR AWS ACCOUNT ID].dkr.ecr.[YOUR AWS ACCOUNT REGION].amazonaws.com/my/test/quarkus-lambda:v1 --role arn:aws:iam::[YOUR AWS ACCOUNT ID]:role/[SOME ROLE]

现在你可以使用AWS控制台来查看和测试你的新lambda函数。

亚马逊Alexa整合

要使用带有Quarkus原生的Alexa ,你需要使用托管在Quarkiverse Hub上的Quarkus Amazon Alexa扩展

<dependency>
    <groupId>io.quarkiverse.alexa</groupId>
    <artifactId>quarkus-amazon-alexa</artifactId>
    <version>${quarkus-amazon-alexa.version}</version> (1)
</dependency>
1 Define the latest version of the extension in your POM file.

Create your Alexa handler, as normal, by sub-classing the abstract com.amazon.ask.SkillStreamHandler, and add your request handler implementation.

That’s all there is to it!