使用 AWS Lambda 部署 Vert.x 原生函数

本操作指南将向您展示如何部署由 AWS Lambda 提供服务的基于 Vert.x 的原生镜像函数

您将构建什么

  • 您将编写一个返回每日名言(类似于 UNIX 工具)的函数。

  • 此函数将使用 Java 编写。

  • GraalVM 的帮助下,代码将被编译为原生可执行文件。

  • 将使用 AWS 命令行界面 创建一个函数。

  • 此函数将使用 AWS Lambda 进行部署。

您需要什么

  • 文本编辑器或 IDE

  • GraalVM (>= 1.0.0-rc13)

  • Maven

  • AWS CLI 工具

  • 用于部署函数的 AWS 账户。

什么是 AWS Lambda 函数以及 Vert.x 为何是理想选择?

AWS Lambda 是一项服务,它负责执行您的代码,而无需您了解服务器环境。它被称为无服务器计算。代码的执行基于对 AWS 服务中事件的响应,例如在 S3 存储桶中添加/删除文件、更新 Amazon DynamoDB 表、来自 Amazon API Gateway 的 HTTP 请求等。

AWS Lambda 代码可以使用 Amazon SDK 以 Java 编写。然而,由于 JVM 和典型 Java 框架的特性,启动时间可能会成为函数性能的巨大瓶颈。为了解决这个问题,通常 Java 函数会在 Java 服务器容器中运行,等待请求(这会使整个无服务器特性失效)。

Vert.x 和 GraalVM 应运而生。Vert.x 非常适合在 GraalVM 原生镜像上编写函数,因为

  1. Vert.x 应用程序启动非常快,因为运行时没有发生“魔法”,

  2. GraalVM 用于编译以进一步减少启动时间和内存占用,

  3. Vert.x 应用程序资源高效,即使在高负载下也能保持响应,

  4. Vert.x 提供了一个庞大的响应式客户端生态系统,可用于其他中间件(数据库、消息传递等),

  5. 单个函数式接口实现足以引导 Vert.x 应用程序!

创建项目

首先克隆以下 GitHub 项目:https://github.com/pmlopes/aws-lambda-native-vertx

之后,让我们回顾一下项目的重要部分。以下是您应该使用的 pom.xml 文件的内容

  1. 我们需要 svm-driver 来处理与 SubstrateVM 的代码不兼容性。

  2. 我们需要 vertx-webclient 来与 Lambda 环境进行交互。

  3. 用于生成镜像的 GraalVM 配置。

编写函数

函数接收传入的 HTTP 头部和 HTTP 正文。选择一个随机的每日名言。对于每个请求,都会发送随机名言

/*
 * Copyright 2019 Paulo Lopes.
 *
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  and Apache License v2.0 which accompanies this distribution.
 *
 *  The Eclipse Public License is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  The Apache License v2.0 is available at
 *  https://open-source.org.cn/licenses/apache2.0.php
 *
 *  You may elect to redistribute this code under either of these licenses.
 */
package lambda;

import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import vertx.lambda.Lambda;

import java.util.Random;

/**
 * A simple QOTD Lambda
 */
public class QOTDLambda implements Lambda {

  private static final Random RANDOM = new Random();

  private static final String[] QUOTES = {    // (1)
    "If you find a random command on the internet, try running it as root. What's the worse that could happen?",
    "Can root create a process that even root can't kill?",
    "There's no place like ~",
    "Thou shalt not kill -9.",
    "The 'n' in unmount is missing, leaving it as umount! If you find it, please write to Bell Labs, 600 Mountain Avenue Murray Hill, NJ.",
    "echo \"Just another useless use of cat.\" | cat",
    "`echo \"Just another useless use of backticks.\"`",
    "We've just created a special character device for handling complaints, conveniently located at /dev/null.",
    "false - do nothing, unsuccessfully"
  };

  @Override
  public
  Future<Buffer>  // (2)
  call(Vertx vertx, MultiMap headers, Buffer body) {    // (3)
    // return a random qotd
    return Future.succeededFuture(Buffer.buffer(QUOTES[RANDOM.nextInt(QUOTES.length)]));  // (4)
  }
}
  1. 我们声明一个名言数组。

  2. 实现 Lambda 接口,它返回一个 Future 以允许异步处理。

  3. 实现接收当前的 Vertx 实例以及请求头部和正文。

  4. 对于每个请求,我们返回一个包含随机名言的已解析的 Future。

测试函数

/*
 * Copyright 2019 Paulo Lopes.
 *
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  and Apache License v2.0 which accompanies this distribution.
 *
 *  The Eclipse Public License is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  The Apache License v2.0 is available at
 *  https://open-source.org.cn/licenses/apache2.0.php
 *
 *  You may elect to redistribute this code under either of these licenses.
 */
package lambda;

import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.core.buffer.Buffer;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.RunTestOnContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(VertxUnitRunner.class)
public class QOTDLambdaTest {

  private final QOTDLambda fn = new QOTDLambda();

  @Rule
  public RunTestOnContext rule = new RunTestOnContext();

  @Test
  public void shouldGetAQOTD(TestContext should) {
    final Async test = should.async();

    Future<Buffer> fut = fn.call(rule.vertx(), MultiMap.caseInsensitiveMultiMap(), null);   // (1)

    fut.setHandler(call -> {
      if (call.failed()) {
        should.fail(call.cause());  // (2)
      } else {
        should.assertNotNull(call.result());  // (3)
        should.assertTrue(call.result().length() > 0);
        test.complete();  // (4)
      }
    });
  }

}
  1. 使用空头部和正文调用函数。

  2. 如果调用失败,则测试失败。

  3. 验证存在名言。

  4. 终止测试。

我们可以轻松测试函数是否正常工作

  1. 从您的 IDE 运行 junit 测试,或

  2. 使用 Maven:mvn test

准备您的 AWS 开发环境

创建 Lambda 角色

在部署到 AWS 之前,您需要拥有一个能够访问服务的角色。为此,请创建一个临时 JSON 文件 (/tmp/trust-policy.json),内容如下

并使用以下命令进行部署

$ aws iam create-role \
    --role-name lambda-role \
    --path "/service-role/" \
    --assume-role-policy-document file:///tmp/trust-policy.json

构建您的函数

构建函数是运行常用的 Maven 命令

$ mvn clean package
$ zip -r function.zip bootstrap target/lambda

最终的 zip 文件是函数使用的基础层。

部署运行时层

此层将安装在 Amazon Linux 的 /opt 目录下。

$ aws lambda publish-layer-version \
    --layer-name vertx-native-example \
    --zip-file fileb://function.zip

这将安装 Lambda 入口点 /opt/bootstrap 以及包含所有函数的原生镜像 ELF。

部署函数

您需要提供一个函数 zip 文件,在我们的案例中,这并不实用,因为所有代码都存在于基础层中,所以我们可以“再次”上传相同的 zip 文件或上传一个空文件。

$ aws lambda create-function --function-name vertxNativeTester \
    --zip-file fileb://function.zip --handler lambda.EchoLambda --runtime provided \
    --role arn:aws:iam::<YOUR-ARN>:role/service-role/lambda-role

如果您的函数需要文件资源,这可能会很有用。在这种情况下,您可以将每个函数的资源打包到单独的 zip 文件中。

由于部署是使用自定义运行时完成的,我们需要将两者关联起来

$ aws lambda update-function-configuration --function-name vertxNativeTester \
    --layers arn:aws:lambda:eu-central-1:<YOUR-ARN>:layer:vertx-native-example:1

测试函数

恭喜您刚刚发布了您的第一个原生函数。为了快速测试它,请使用工具包

$ aws lambda invoke --function-name vertxNativeTester \
    --payload '{"message":"Hello World"}' \
    --log-type Tail response.txt | grep "LogResult" | awk -F'"' '{print $4}' | base64 --decode

总结

  • 我们使用 Vert.x 编写了一个返回每日名言 (QOTD) 的原生函数。

  • 我们构建了一个 AWS Lambda 包。

  • 我们使用 AWS CLI 部署了此函数。

另请参阅


最后发布时间:2021-06-14 19:44:28 +0000。