使用 Lambda SnapStart 的最佳实践 - Amazon Lambda
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

使用 Lambda SnapStart 的最佳实践

网络连接

当 Lambda 从快照恢复您的函数时,无法保证您的函数在初始化阶段建立的连接的状态。在大多数情况下,Amazon 开发工具包建立的网络连接会自动恢复。对于其他连接,我们建议您遵循以下最佳实践。

重新建立网络连接

函数从快照恢复时,请务必重新建立网络连接。我们建议您在函数处理程序中重新建立网络连接。或者,您可以使用 afterRestore 运行时挂钩

不要使用主机名作为唯一的执行环境标识符

我们建议不要使用 hostname 将执行环境标识为应用程序中的唯一节点或容器。利用 SnapStart,使用单个快照作为多个执行环境的初始状态,并且所有执行环境都为 InetAddress.getLocalHost() 返回相同的 hostname 值。对于需要唯一执行环境标识或 hostname 值的应用程序,我们建议您在函数处理程序中生成唯一的 ID。或者,使用 afterRestore 运行时挂接生成唯一的 ID,然后使用该唯一 ID 作为执行环境的标识符。

避免将连接绑定到固定源端口

我们建议您避免将网络连接绑定到固定源端口。函数从快照恢复时,会重新建立连接,绑定到固定源端口的网络连接可能会失败。

避免使用 Java DNS 缓存

Lambda 函数已经缓存了 DNS 响应。如果您将另一个 DNS 缓存与 SnapStart 结合使用,则函数从快照恢复时可能会出现连接超时。

java.util.logging.Logger 类可以间接启用 JVM DNS 缓存。要覆盖默认设置,请在初始化 logger 之前将 networkaddress.cache.ttl 设置为 0。例如:

public class MyHandler { // first set TTL property static{ java.security.Security.setProperty("networkaddress.cache.ttl" , "0"); } // then instantiate logger var logger = org.apache.logging.log4j.LogManager.getLogger(MyHandler.class); }

为防止 UnknownHostException 失败,建议将 networkaddress.cache.negative.ttl 设置为 0。您可以使用 AWS_LAMBDA_JAVA_NETWORKADDRESS_CACHE_NEGATIVE_TTL=0 环境变量为 Lambda 函数设置此属性。

禁用 JVM DNS 缓存并不能禁用 Lambda 的托管式 DNS 缓存。

性能优化

注意

SnapStart 在与大规模函数调用搭配使用时效果最佳。不经常调用的函数可能无法获得相同的性能改进。

为了最大限度地发挥 SnapStart 的优势,我们建议您在初始化代码中预加载导致启动延迟的类,而不是在函数处理程序中加载。这会将与大量类加载相关的延迟移出调用路径,从而优化了 SnapStart 的启动性能。

如果您在初始化期间无法预加载类,那么我们建议您使用虚拟调用预加载类。为此,请更新函数处理程序代码,如 Amazon Labs GitHub 存储库上宠物商店函数的以下示例所示。

private static SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler; static { try { handler = SpringLambdaContainerHandler.getAwsProxyHandler(PetStoreSpringAppConfig.class); // Use the onStartup method of the handler to register the custom filter handler.onStartup(servletContext -> { FilterRegistration.Dynamic registration = servletContext.addFilter("CognitoIdentityFilter", CognitoIdentityFilter.class); registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*"); }); // Send a fake Amazon API Gateway request to the handler to load classes ahead of time ApiGatewayRequestIdentity identity = new ApiGatewayRequestIdentity(); identity.setApiKey("foo"); identity.setAccountId("foo"); identity.setAccessKey("foo"); AwsProxyRequestContext reqCtx = new AwsProxyRequestContext(); reqCtx.setPath("/pets"); reqCtx.setStage("default"); reqCtx.setAuthorizer(null); reqCtx.setIdentity(identity); AwsProxyRequest req = new AwsProxyRequest(); req.setHttpMethod("GET"); req.setPath("/pets"); req.setBody(""); req.setRequestContext(reqCtx); Context ctx = new TestContext(); handler.proxy(req, ctx); } catch (ContainerInitializationException e) { // if we fail here. We re-throw the exception to force another cold start e.printStackTrace(); throw new RuntimeException("Could not initialize Spring framework", e); } }