在 AWS 上使用 Lambda(Java 25)、API Gateway 和 DynamoDB 的无服务器应用程序 - 第 1 部分 示例应用
Source: Dev.to
在本系列文章中,我们将说明如何使用 Lambda 以及新发布的 Java 25 运行时在 AWS 上实现无服务器应用。
我们还会使用 API Gateway、DynamoDB 和 AWS SAM 来进行基础设施即代码(IaC)的定义。
本系列分为三部分:
- 基线 – 部署应用并在没有任何优化的情况下测量冷启动/热启动时间。
- 冷启动降低 – 探索 Lambda SnapStart、预热以及 GraalVM Native Image 等技术。
- 扩展示例 – 伴随系列,将 DynamoDB 替换为关系型无服务器 Amazon Aurora DSQL 数据库,并使用 Hibernate ORM。
你可以在 GitHub 上找到示例代码:aws‑lambda‑java‑25‑dynamodb。
架构概览
示例应用提供了一个简单的产品目录:
- 创建 产品(POST)
- 检索 按 ID 获取产品(GET)
它使用了以下 AWS 服务:
| Service | Role(角色) |
|---|---|
| Amazon API Gateway | 为 Lambda 函数公开 REST API |
| AWS Lambda | 执行 Java 25 代码(或 GraalVM 本地镜像) |
| Amazon DynamoDB | 用于产品数据的 NoSQL 持久化层 |
| AWS SAM | 整个堆栈的声明式基础设施即代码(IaC) |
假设: 您对上述服务以及 AWS 上的无服务器架构有基本了解。
前置条件
| 工具 | 版本 |
|---|---|
| Java | 25 |
| Maven | latest |
| AWS CLI | latest |
| SAM CLI | latest |
| 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: getPost‑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,并返回 404 或 200 响应,响应体为 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记录。
下一步
- 部署 SAM 堆栈(
sam build && sam deploy)。 - 调用 API 端点并记录冷/热启动时间。
- 启用 SnapStart(取消注释
SnapStart块)并重新测量。 - 构建 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 上为我的仓库加星
- 关注我,以获取更多技术文章和即将举行的公开演讲活动
- 访问我的个人网站获取更多资源
谢谢!