使用Amazon LambdaAmazon Neptune 中的函数 - Amazon Neptune
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 Amazon Web Services 服务入门

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

使用Amazon LambdaAmazon Neptune 中的函数

Amazon Lambda函数在 Amazon Neptune 应用程序中有很多用途。在这里,我们提供了将 Lambda 函数与任何流行的 Gremlin 驱动程序和语言变体一起使用的一般指导,以及使用 Java、JavaScript 和 Python 编写的 Lambda 函数的具体示例。

注意

在 Neptune 中使用 Lambda 函数的最佳方式随着最近发布的引擎发生了变化。Neptune 曾经在 Lambda 执行上下文被回收后很长时间保持空闲连接处于打开状态,从而可能导致服务器上的资源泄漏。为了缓解这种情况,我们曾经建议在每次 Lambda 调用时打开和关闭连接。但是,从引擎版本 1.0.3.0 开始,空闲连接超时已减少,以便在回收非活动 Lambda 执行上下文后连接不再泄漏,因此我们现在建议在执行上下文的持续时间内使用单个连接。这应该包括一些错误处理和回退并重试样板代码来处理意外关闭的连接。

管理格林林 WebSocket 连接Amazon Lambda函数

如果您使用格雷姆林语言变体来查询 Neptune,驱动程序将使用 WebSocket 连接连接到数据库。WebSockets 旨在支持长寿命的客户端-服务器连接方案。Amazon Lambda另一方面, 是为了支持相对短暂和无国籍的处决. 在使用 Lambda 查询 Neptune 时,设计理念中的这种不匹配可能会导致一些意想不到的问题。

一个Amazon Lambda函数运行在执行上下文,它将函数与其他函数隔离开来。执行上下文是在第一次调用函数时创建的,并可以重复用于后续调用同一函数。

但是,任何一个执行上下文都不会用于处理函数的多个并发调用。如果您的函数被多个客户端同时调用,则 Lambda旋转一个额外的执行上下文对于函数的每个实例。反过来,所有这些新执行上下文可能会被重用于后续的函数调用。

在某些时候,Lambda 会回收执行上下文,特别是如果它们已经处于非活动状态一段时间。Amazon Lambda公开了执行上下文生命周期,包括InitInvokeShutdown阶段,通过Lambda 扩展。使用这些扩展,您可以编写代码,以便在回收执行上下文时清理外部资源(如数据库连接)。

一个常见的最佳做法是在 Lambda 处理程序函数之外打开数据库连接,以便它可以在每个处理程序调用中重复使用。如果数据库连接在某个时刻断开,则可以从处理程序内部重新连接。但是,这种方法存在连接泄漏的危险。如果空闲连接在执行上下文被销毁后很长时间保持打开状态,则间歇性或突发性 Lambda 调用方案可能会逐渐泄漏连接并耗尽数据库资源。

随着更新的引擎版本,Neptune 连接限制和连接超时发生了变化。以前,每个实例最多支持 60,000 个 WebSocket 连接。现在,每个 Neptune 实例的最大并发 WebSocket 连接数因实例类型而异

此外,从发动机 1.0.3.0 开始,Neptune 将连接的空闲超时从 1 小时减少到大约 20 分钟。如果客户端未关闭连接,则连接将在 20-25 分钟空闲超时后自动关闭。Amazon Lambda不记录执行上下文生命周期,但实验表明新的 Neptune 连接超时与非活动的 Lambda 执行上下文超时非常一致。当一个非活动的执行上下文被回收时,很可能它的连接已经被 Neptune 关闭,或者很快就会关闭。

使用建议Amazon Lambda与 Amazon Neptune Gemlin

我们现在建议在 Lambda 执行上下文的整个生命周期内使用单个连接和图形遍历源,而不是用于每个函数调用(每个函数调用只处理一个客户端请求)。由于并发客户端请求由在单独的执行上下文中运行的不同函数实例处理,因此无需维护连接池来处理函数实例内的并发请求。如果您正在使用的 Gemlin 驱动程序具有连接池,请将其配置为仅使用一个连接。

要处理连接失败,请在每个查询周围使用重试逻辑。即使目标是在执行上下文的生命周期内维护单个连接,意外的网络事件也会导致该连接突然终止。此类连接失败表现为不同的错误,具体取决于您使用的驱动程序。您应该编写 Lambda 函数来处理这些连接问题,并在必要时尝试重新连接。

一些 Gemlin 驱动程序会自动处理重新连接。例如,Java 驱动程序会自动尝试代表您的客户端代码重新建立到 Neptune 的连接。使用此驱动程序,您的函数代码只需要退出并重试查询。相比之下,JavaScript 和 Python 驱动程序不实现任何自动重新连接逻辑,因此使用这些驱动程序,您的函数代码必须在退出后尝试重新连接,并且只有在重新建立连接后重试查询。

这里的代码示例确实包括重新连接逻辑,而不是假设客户端正在处理它。

在 Lambda 中使用格雷姆林写请求的建议

如果您的 Lambda 函数修改图形数据,请考虑采用回退并重试策略来处理以下异常:

  • ConcurrentModificationException— Neptune 事务语义意味着写入请求有时会失败并且ConcurrentModificationException。在这些情况下,请尝试基于指数退出的重试机制。

  • ReadOnlyViolationException— 由于群集拓扑随时可能会因计划内或计划外事件而发生变化,写入责任可能会从群集中的一个实例迁移到另一个实例。如果您的函数代码尝试将写入请求发送到不再是主(写入器)实例的实例,请求将失败并显示ReadOnlyViolationException。发生这种情况时,请关闭现有连接,重新连接到群集终端节点,然后重试请求。

此外,如果您使用退出并重试策略来处理写入请求问题,请考虑为创建和更新请求实现幂等查询(例如,使用折叠 () .coalesce (). 展开 ()

在 Lambda 中使用格雷姆林读取请求的建议

如果您的群集中有一个或多个只读副本,最好在这些副本之间平衡读取请求。一种选择是使用读者终端节点。即使在添加或删除复制副本时群集拓扑发生了变化,或者将复制副本提升为新的主实例,读取器终端节点也会平衡复制副本之间的连接。

但是,在某些情况下,使用读取器终端节点可能会导致群集资源的使用不均衡。读取器终端节点的运行方式是定期更改 DNS 条目指向的主机。如果客户端在 DNS 条目更改之前打开了大量连接,则所有连接请求都会发送到单个 Neptune 实例。高吞吐量 Lambda 场景就是这种情况,其中对 Lambda 函数的大量并发请求会导致创建多个执行上下文,每个上下文都有自己的连接。如果这些连接几乎都是同时创建的,则这些连接很可能都指向群集中的同一个复制副本,并且一直指向该复制副本,直到执行上下文被回收为止。

您可以跨实例分配请求的一种方法是将 Lambda 函数配置为连接到从副本实例终端节点列表中随机选择的实例终端节点,而不是读取器终端节点。此方法的缺点是,它需要 Lambda 代码通过监视集群并在群集成员资格发生变化时更新终端节点列表来处理群集拓扑中的更改。

如果您正在编写一个 Java Lambda 函数,该函数需要平衡集群中的实例中的读取请求,则可以使用Amazon Neptune 的 Gemlin 客户端,它是一个了解您的集群拓扑的 Java Gemlin 客户端,它可以在 Neptune 集群中的一组实例之间公平地分布连接和请求。此博客帖子包含一个示例 Java Lambda 函数,该函数使用 Amazon Neptune 的格雷姆林客户端。

可能会减慢 Neptune 格雷姆林 Lambda 函数冷启动速度的因素

第一次Amazon Lambda函数被称为冷启动。有几个因素可能会增加冷启动的延迟:

  • 请务必为 Lambda 函数分配足够的内存。   — 冷启动期间编译 Lambda 函数的速度可能比 EC2 上的速度慢得多,因为Amazon Lambda分配 CPU 周期与内存成比例的线性您将其分配给函数的处理能力。使用 1,792 MB 内存时,函数接收相当于一个完整 vCPU(每秒一个 vCPU 秒的积分)的处理能力。对于使用 Java 编写的大型 Lambda 函数,未分配足够的内存以接收足够 CPU 周期的影响尤其明显。

  • 请注意,启用 IAM 数据库身份验证可能会减慢冷启动–Amazon Identity and Access Management(IAM) 数据库身份验证也可以减慢冷启动的速度,特别是当 Lambda 函数必须生成新的签名密钥时。此延迟仅影响冷启动,而不会影响后续请求,因为一旦 IAM DB 身份验证建立了连接凭证,Neptune 只会定期验证它们是否仍然有效。