我正在尝试使用 Java 11(或 17)AWS CDK 来编排 Lambda。 Lambda 在 JVM 编译和冷启动方面存在严重问题,因此我一直在考虑使用 AWS 提供的较新的 SnapStart 功能。 CDK 推送包含 SnapStart 组件的 CloudFormation 堆栈、新版本的 Lambda 以及该版本的别名。这些都是 SnapStart 的必要要求。
使用 SnapStart 配置和别名创建 CDK Lambda
//Lambda Function
String lambdaHandlerName = "com.handlers.lambdaHandler::handleRequest";
IRole role5 = Role.fromRoleName(this, "Lambda", "LambdaRole");
Function lambdaFunction = new Function(this, "lambdaFunction", getLambdaFunctionProps(lambdaEnvMap, lambdaHandlerName, role5, "lambdaHandler"));
dynamoTable.grantReadData(lambdaFunction);
//SnapStart Config
lambdaFunction.getCurrentVersion().addAlias("snap", AliasOptions.builder()
.description("Alias version for snap resources")
.build());
CfnFunction lambdaCfnFunction = (CfnFunction) lambdaCfnFunction.getNode().getDefaultChild();
lambdaCfnFunction.setSnapStart(CfnFunction.SnapStartProperty.builder()
.applyOn("PublishedVersions")
.build());
在 AWS 控制台本身内,似乎已为设置为“PublishedVersions”的基本功能启用了 SnapStart。
当我查看最近发布的版本时,我发现它也启用了 SnapStart。
但是,当我通过 API Gateway(配置为正确版本的 Lambda)调用 Lambda 函数时,SnapStart 似乎不适用于该函数,并且我的冷启动时间仍然约为 2 秒。
我还尝试实现 AWS 建议的 SnapStart 运行时挂钩到 Lambda 函数处理程序本身:
SnapStart 的处理程序运行时挂钩
public GetAllQuotesHandler() {
Core.getGlobalContext().register(this);
}
@Override
public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent event, Context context) {
...
}
@Override
public void beforeCheckpoint(org.crac.Context<? extends Resource> context) throws Exception {
this.dynamoDb.close();
System.out.println("Before");
}
@Override
public void afterRestore(org.crac.Context<? extends Resource> context) throws Exception {
System.out.println("After");
}
}
我不确定我的配置有什么问题,有什么建议吗?
我希望 AWS Lambda 能够使用 SnapStart 并调用我的 Lambda 函数,而无需冷启动。
到目前为止,您似乎已经在 lambda 配置上配置了 SnapStart,这本身将使您的冷启动时间略有减少。为了完全减少冷启动时间,您需要在创建 lambda 版本时拍摄快照之前“准备”JVM 的状态。
beforeCheckpoint
运行时钩子是在拍摄 JVM/容器快照之前调用的。通过从此方法中调用代码单元,JVM 将解释这些单元存储的字节码 .class 文件,并使用 JIT 编译将它们编译为本机机器代码。这将保存在快照中,因此当首次为实际请求调用 lambda 时,不需要解释该字节码。
使用代码执行此操作的最简单方法是从
handleRequest(…)
方法中调用 beforeCheckpoint(…)
方法,并使用 APIGatewayProxyRequestEvent
来完成您拥有的任何业务逻辑,并实际调用 dynamoDb 客户端。理想情况下,您将调用尽可能多的代码,以便在拍摄快照之前对其进行解释。
所以它看起来像这样:
public class GetAllQuotesHandler {
public GetAllQuotesHandler() {
Core.getGlobalContext().register(this);
}
@Override
public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent event, Context context) {
...
}
// Call the handler method with a test event.
@Override
public void beforeCheckpoint(org.crac.Context<? extends Resource> context) throws Exception {
log.info("beforeCheckpoint({}) called.", context);
APIGatewayProxyRequestEvent testEvent = new APIGatewayProxyRequestEvent().withBody("string body").withHttpMethod("POST");
try {
handleRequest(testEvent, null);
} catch (Exception ex) {
log.info("Error executing beforeCheckpoint({})", testEvent);
}
}
@Override
public void afterRestore(org.crac.Context<? extends Resource> context) throws Exception {
// not used
}
}
如果您的函数使用 aws SDK 调用其他 AWS 资源,您还需要使用不同的凭证提供程序:
ContainerCredentialsProvider.builder().build();
因为这些资源需要最新的凭据。您无法使用环境变量凭据提供程序,因为这些凭据仅在创建版本时才会被调用,并且它们最终将过期。这是因为无参数构造函数只会在创建版本时调用一次,而不是每次在新的执行环境中调用 lambda 函数时调用。