在 AWS 上使用 Lambda(Java 25)、API Gateway 和 DynamoDB 的无服务器应用程序 - 第 1 部分 示例应用

发布: (2026年3月16日 GMT+8 23:31)
7 分钟阅读
原文: Dev.to

Source: Dev.to

在本系列文章中,我们将说明如何使用 Lambda 以及新发布的 Java 25 运行时在 AWS 上实现无服务器应用。
我们还会使用 API GatewayDynamoDBAWS SAM 来进行基础设施即代码(IaC)的定义。

本系列分为三部分:

  1. 基线 – 部署应用并在没有任何优化的情况下测量冷启动/热启动时间。
  2. 冷启动降低 – 探索 Lambda SnapStart、预热以及 GraalVM Native Image 等技术。
  3. 扩展示例 – 伴随系列,将 DynamoDB 替换为关系型无服务器 Amazon Aurora DSQL 数据库,并使用 Hibernate ORM。

你可以在 GitHub 上找到示例代码:aws‑lambda‑java‑25‑dynamodb

架构概览

示例应用提供了一个简单的产品目录:

  • 创建 产品(POST)
  • 检索 按 ID 获取产品(GET)

它使用了以下 AWS 服务:

ServiceRole(角色)
Amazon API Gateway为 Lambda 函数公开 REST API
AWS Lambda执行 Java 25 代码(或 GraalVM 本地镜像)
Amazon DynamoDB用于产品数据的 NoSQL 持久化层
AWS SAM整个堆栈的声明式基础设施即代码(IaC)

假设: 您对上述服务以及 AWS 上的无服务器架构有基本了解。

前置条件

工具版本
Java25
Mavenlatest
AWS CLIlatest
SAM CLIlatest
GraalVM(可选,用于本地镜像)latest with native-image component

我们将首先构建并部署 Java 25 版本,随后再编译 GraalVM 本地镜像,并使用 custom runtime 运行它。

AWS SAM 模板 – 基础设施即代码(IaC)要点

以下是 template.yaml 的相关部分。仅显示影响 Lambda 函数的章节。

全局函数设置

Globals:
  Function:
    CodeUri: .
    Runtime: java25
    # SnapStart (uncomment to enable)
    # SnapStart:
    #   ApplyOn: PublishedVersions
    Timeout: 30
    MemorySize: 1024
    Architectures:
      - x86_64
    Environment:
      Variables:
        REGION: !Sub ${AWS::Region}
        PRODUCT_TABLE_NAME: !Ref ProductsTable
        # … other variables …

Get‑Product‑By‑Id 函数

GetProductByIdFunction:
  Type: AWS::Serverless::Function
  Properties:
    FunctionName: GetProductByIdJava25WithDynamoDB
    AutoPublishAlias: liveVersion
    Handler: software.amazonaws.example.product.handler.GetProductByIdHandler::handleRequest
    Policies:
      - DynamoDBReadPolicy:
          TableName: !Ref ProductsTable
    Events:
      GetRequestById:
        Type: Api
        Properties:
          RestApiId: !Ref MyApi
          Path: /products/{id}
          Method: get

Post‑Product 函数

PostProductJava25WithDynamoDB 的定义遵循相同的模式(此处省略)。

Lambda 处理程序 – GetProductByIdHandler

@Override
public APIGatewayProxyResponseEvent handleRequest(
        APIGatewayProxyRequestEvent requestEvent,
        Context context) throws JsonProcessingException {

    var id = requestEvent.getPathParameters().get("id");
    var optionalProduct = productDao.getProduct(id);

    if (optionalProduct.isEmpty()) {
        return new APIGatewayProxyResponseEvent()
                .withStatusCode(HttpStatusCode.NOT_FOUND)
                .withBody("Product with id = " + id + " not found");
    }

    return new APIGatewayProxyResponseEvent()
            .withStatusCode(HttpStatusCode.OK)
            .withBody(objectMapper.writeValueAsString(optionalProduct.get()));
}
  • 该方法接收一个 APIGatewayProxyRequestEvent(由 API Gateway 调用)。
  • 它提取路径参数 id,通过 productDao.getProduct(id) 查询 DynamoDB,并返回 404200 响应,响应体为 JSON 数据。

CreateProductHandler(用于 POST)遵循相同的结构。

领域模型 – 产品实体

public record Product(String id, String name, BigDecimal price) {}

一个表示产品的简单不可变记录。

Persistence Layer – ProductDao(摘录)

public class ProductDao {

    private final DynamoDbClient dynamoDb;
    private final String tableName;
    private final ObjectMapper objectMapper = new ObjectMapper();

    public ProductDao(DynamoDbClient dynamoDb, String tableName) {
        this.dynamoDb = dynamoDb;
        this.tableName = tableName;
    }

    public Optional getProduct(String id) {
        GetItemResponse response = dynamoDb.getItem(
                GetItemRequest.builder()
                        .tableName(tableName)
                        .key(Map.of("id", AttributeValue.builder().s(id).build()))
                        .build());

        if (response.hasItem()) {
            var item = response.item();
            var product = objectMapper.convertValue(item, Product.class);
            return Optional.of(product);
        }
        return Optional.empty();
    }

    // … methods for putItem, deleteItem, etc. …
}
  • 使用 AWS SDK for Java 2.x 与 DynamoDB 交互。
  • getProduct 构建 GetItemRequest,执行请求,并将结果映射回 Product 记录。

下一步

  1. 部署 SAM 堆栈(sam build && sam deploy)。
  2. 调用 API 端点并记录冷/热启动时间。
  3. 启用 SnapStart(取消注释 SnapStart 块)并重新测量。
  4. 构建 GraalVM 本地镜像 并将其部署为自定义运行时,以进一步降低冷启动时间。

敬请期待后续文章,我们将深入探讨每种优化技术!

从 DynamoDB 检索产品

GetItemResponse getItemResponse = dynamoDbClient.getItem(
    GetItemRequest.builder()
        .key(Map.of("PK", AttributeValue.builder().s(id).build()))
        .tableName(PRODUCT_TABLE_NAME)
        .build());

if (getItemResponse.hasItem()) {
    return Optional.of(ProductMapper.productFromDynamoDB(getItemResponse.item()));
} else {
    return Optional.empty();
}

下面我们使用 DynamoDbClient 实例构建 GetItemRequest,查询 DynamoDB 表。
表名通过环境变量(在 AWS SAM 模板中设置)使用 System.getenv("PRODUCT_TABLE_NAME") 获取。
如果找到产品,自定义的 ProductMapper 会将 DynamoDB 项目转换为 Product 实体。

构建与部署

# Build the application
mvn clean package

# Deploy with SAM (guided mode)
sam deploy -g

部署完成后,SAM 会返回一个 自定义的 Amazon API Gateway URL。该 URL 用于创建和检索产品。API 通过 API 密钥进行保护,必须在 X-API-Key 请求头中发送该密钥(参见 template.yaml 中的 MyApiKey 定义)。

API 密钥: a6ZbcDefQW12BN56WEVDDB25

示例请求

创建产品 (ID = 1)

curl -X PUT \
     -d '{ "id": 1, "name": "Print 10x13", "price": 0.15 }' \
     -H "X-API-Key: a6ZbcDefQW12BN56WEVDDB25" \
     https://{API_GATEWAY_URL}/prod/products

检索产品 (ID = 1)

curl -H "X-API-Key: a6ZbcDefQW12BN56WEVDDB25" \
     https://{API_GATEWAY_URL}/prod/products/1

(将 {API_GATEWAY_URL} 替换为 sam deploy 返回的 URL。)

接下来是什么?

在本文中,我们介绍了示例应用程序。在下一篇文章中,我们将在没有任何优化的情况下测量 Lambda 函数的冷启动和热启动时间

随后,我们将使用 关系型、无服务器的 Amazon Aurora(PostgreSQL)数据库Hibernate ORM 一起进行相同的 Lambda 性能测量。

如果您喜欢这些内容,请:

  • ⭐ 在 GitHub 上为我的仓库加星
  • 关注我,以获取更多技术文章和即将举行的公开演讲活动
  • 访问我的个人网站获取更多资源

谢谢!

0 浏览
Back to Blog

相关文章

阅读更多 »