为 AWS Lambda 缩短 SDK 启动时间
AWS SDK for Java 2.x 的目标之一是降低 AWS Lambda 函数的启动延迟。SDK 包含可缩短启动时间的更改,本主题末尾将对此进行讨论。
首先,本主题重点介绍为缩短冷启动时间可以进行的更改。这包括更改代码结构和服务客户端的配置。
使用基于 AWS CRT 的 HTTP 客户端
为了与 AWS Lambda 配合使用,我们建议在同步场景中使用 AwsCrtHttpClientAwsCrtAsyncHttpClient
本指南中的 配置基于 AWS CRT 的 HTTP 客户端 主题描述了使用 HTTP 客户端的好处、如何添加依赖项以及如何按服务客户端配置其使用。
移除未使用的 HTTP 客户端依赖项
除了明确使用基于 AWS CRT 的客户端之外,您还可以移除 SDK 默认引入的其他 HTTP 客户端。当需要加载的库较少时,Lambda 启动时间会缩短,因此您应该移除 JVM 需要加载的所有未使用的构件。
以下 Maven pom.xml 文件片段展示了排除基于 Apache 的 HTTP 客户端和基于 Netty 的 HTTP 客户端的情况。(使用基于 AWS CRT 的客户端时不需要这些客户端。) 此示例从 S3 客户端依赖项中排除 HTTP 客户端构件,并添加 aws-crt-client 构件以允许访问基于 AWS CRT 的 HTTP 客户端。
<project> <properties> <aws.java.sdk.version>2.27.21</aws.java.sdk.version> <properties> <dependencyManagement> <dependencies> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>bom</artifactId> <version>${aws.java.sdk.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>aws-crt-client</artifactId> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>s3</artifactId> <exclusions> <exclusion> <groupId>software.amazon.awssdk</groupId> <artifactId>netty-nio-client</artifactId> </exclusion> <exclusion> <groupId>software.amazon.awssdk</groupId> <artifactId>apache-client</artifactId> </exclusion> </exclusions> </dependency> </dependencies> </project>
注意
将 <exclusions> 元素添加到 pom.xml 文件中的所有服务客户端依赖项中。
配置服务客户端以进行快捷查找
- 指定区域
-
创建服务客户端时,在服务客户端生成器上调用
region方法。这可简化 SDK 的默认区域查找过程,该过程会在多个位置查找 AWS 区域 信息。要使 Lambda 代码独立于区域,请在
region方法中使用以下代码。此代码访问 Lambda 容器设置的AWS_REGION环境变量。Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable())) - 使用
EnvironmentVariableCredentialProvider -
与区域信息的默认查找行为非常相似,SDK 会在多个位置查找凭证。通过在生成服务客户端时指定
EnvironmentVariableCredentialProvider,可以节省 SDK 凭证查找过程的时间。 注意
利用此凭证提供程序,代码能够在 Lambda 函数中使用,但可能不适用于 Amazon EC2 或其他系统。
如果您打算在某个时候使用适用于 Java 的 Lambda SnapStart,则应依靠默认的凭证提供程序链来查找凭证。如果您指定
EnvironmentVariableCredentialsProvider,则初始凭证查找会起作用,但是当激活 SnapStart 时,Java 运行时会设置容器凭证环境变量。激活后,EnvironmentVariableCredentialsProvider使用的环境变量(访问密钥环境变量)对 Java SDK 不可用。
以下代码段显示了为在 Lambda 环境中使用而经过适当配置的 S3 服务客户端。
S3Client s3Client = S3Client.builder() .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) .httpClient(AwsCrtHttpClient.builder().build()) .build();
在 Lambda 函数处理程序之外初始化 SDK 客户端
我们建议在 Lambda 处理程序方法之外初始化 SDK 客户端。这样,如果重复使用执行上下文,则可以跳过服务客户端的初始化。通过重复使用客户端实例及其连接,处理程序方法的后续调用可更快进行。
在以下示例中,使用静态工厂方法在构造函数中初始化 S3Client 实例。如果重复使用由 Lambda 环境管理的容器,则会重复使用初始化的 S3Client 实例。
public class App implements RequestHandler<Object, Object> { private final S3Client s3Client; public App() { s3Client = DependencyFactory.s3Client(); } @Override public Object handle Request(final Object input, final Context context) { ListBucketResponse response = s3Client.listBuckets(); // Process the response. } }
尽量减少依赖关系注入
依赖关系注入 (DI) 框架可能需要更多时间才能完成设置过程。它们可能还需要额外的依赖项,这需要一段时间才能加载。
如果需要 DI 框架,建议使用诸如 Dagger
使用针对 AWS Lambda 的 Maven 原型
AWS Java SDK 团队开发了一个 Maven 原型
要了解有关原型的更多信息并完成示例部署,请参阅此博客文章
考虑适用于 Java 的 Lambda SnapStart
如果您的运行时系统要求兼容,则 AWS 提供了适用于 Java 的 Lambda SnapStart。Lambda SnapStart 是一种基于基础设施的解决方案,可提高 Java 函数的启动性能。当您发布函数的新版本时,Lambda SnapStart 会对其进行初始化,并拍摄内存和磁盘状态的不可变的加密快照。然后,SnapStart 会缓存快照以供重复使用。
影响启动时间的 2.x 版更改
除了您对代码所做的更改外,适用于 Java 的 SDK 2.x 版本还包括三项可缩短启动时间的主要更改:
-
使用 jackson-jr
,它是一个序列化库,可以改进初始化时间 -
对日期和时间对象使用 java.time
库,此为 JDK 的一部分。 -
对记录 facade 使用 Slf4j
。
其他资源
《AWS Lambda 开发人员指南》中有一节介绍开发非特定于 Java 的 Lambda 函数的最佳实践。
有关通过 AWS Lambda 使用 Java 构建云原生应用程序的示例,请参阅此 研讨会内容
您可以考虑使用提前编译的静态映像来减少启动延迟。例如,您可以使用适用于 Java 的 SDK 2.x 和 Maven 来构建 GraalVM 原生映像。