在 AWS Lambda 中使用 Powertools 实现幂等性函数
重要要点
AWS Lambda 服务会对失败的函数进行重试,因此幂等性在处理支付或订单时至关重要。本文讲解了如何使用 Powertools 的幂等性工具,使 Lambda 函数保持幂等性。了解幂等性的概念,有助于避免重复处理事件,减少客户体验中的问题。本篇文章主要讲述了 AWS Lambda 中幂等函数的实现,帮助开发者避免重复支付或订单处理所带来的负面影响。幂等性是指一个操作可以多次应用而不改变结果的特性。在 Lambda 函数中,这一特性能确保即便出现了多次事件调用,任务也只会执行一次。
本文章将深入探讨幂等性的概念及其在 AWS Lambda 中的实现,并且介绍如何利用 Powertools 的幂等性工具来简化过程。Powertools 的幂等性工具已由 Vanguard 联合开发,现在已经可以使用。
理解幂等性
幂等性是指一个操作的特性,即它可以多次执行而不会改变最初执行的结果。安全地多次执行幂等操作,不会产生副作用,例如重复记录或数据不一致性。这一特性在处理支付和订单处理或与第三方 API 集成时尤其重要。
在 AWS Lambda 中实现幂等性时需要考虑一些关键概念。每次调用时,必须指定用于识别幂等请求的事件负载子集,这个子集称为 幂等性键。这个键可以是一个单一字段,比如 transactionId,或者是多个字段的组合,比如 customerId 与 requestId,甚至整个事件负载。
由于事件负载中的时间戳、日期和其他生成的值会影响幂等性键,因此我们建议使用特定字段来定义,而不是整个事件负载。
通过评估幂等性键,您可以决定函数是否需要重新执行,或者将已有的响应发送给客户端。为此,您需要将每个请求的以下信息存储在持久性层中例如:Amazon DynamoDB:
属性描述状态INPROGRESS、EXPIRED、COMPLETE响应数据需要发回给客户的响应,而不是再次执行函数到期时间戳幂等性记录在何时变得不再有效以下图示显示了幂等性场景中的成功请求流:
当您第一次以特定事件调用 Lambda 函数时,它会在持久性层中存储一个与事件负载相关联的唯一幂等性键记录。函数执行代码并用函数响应更新持久性层中的记录。对于后续使用相同负载的调用,需要检查该幂等性键是否存在于持久性层中。如果存在,函数会将相同的响应返回给客户端。这样就防止了函数的多次调用,确保了幂等性。
还有许多边缘情况需要注意,例如幂等性记录过期,或在客户端、Lambda 函数和持久性层之间的故障处理。相关的详细请参见 Powertools for AWS Lambda (TypeScript) 文档。
在 AWS Lambda 中利用 Powertools 实现幂等性
Powertools for AWS Lambda 提供了多种语言的支持,例如: Python Java NET 以及 TypeScript,提供便捷的工具以促进最佳实践并降低实现重复任务所需的代码量。特别是,它提供了一个模块来处理幂等性。
本文将使用 Powertools 的 TypeScript 版本示例。要开始使用 Powertools 幂等性模块,您需要先安装该库并在构建过程中进行配置。获取更多详情,请参考 Powertools for AWS Lambda 文档。
入门
Powertools for AWS Lambda (TypeScript) 是模块化的,这意味著您可以独立于 Logger、Tracing、Metrics 或其他包安装幂等性工具。使用 npm 安装幂等性工具库及 AWS SDK v3 客户端以支持 DynamoDB:
bashnpm i @awslambdapowertools/idempotency @awssdk/clientdynamodb @awssdk/libdynamodb
在开始之前,您需要创建一个持久化存储层,以便幂等性工具可以存储其状态。您的 Lambda 函数 AWS 身份与访问管理 (IAM) 角色必须具备 dynamodbGetItem、dynamodbPutItem、dynamodbUpdateItem 和 dynamodbDeleteItem 的权限。
目前,DynamoDB 是唯一支持的持久化存储层,因此您需要先创建一个表。可以使用 AWS Cloud Development Kit (CDK)、 AWS CloudFormation、 AWS 无伺服器应用程式模型 (SAM) 或任何支持 DynamoDB 资源的基础设施代码工具创建该表。
接下来的几个部分将说明如何通过包装函数或使用 middy 中介软体将 Lambda 函数的代码加以仪器化,以使其变得幂等。
使用函数包装器
假设您已创建名为 IdempotencyTable 的 DynamoDB 表,在 Lambda 函数代码中创建持久化层:
typescriptimport { makeIdempotent } from @awslambdapowertools/idempotencyimport { DynamoDBPersistenceLayer } from @awslambdapowertools/idempotency/dynamodb
const persistenceStore = new DynamoDBPersistenceLayer({ tableName IdempotencyTable})
现在,将 makeIdempotent 函数包装器应用于您的 Lambda 函数处理程序中,使其变得幂等,并使用之前配置的持久性存储:
typescriptimport { makeIdempotent } from @awslambdapowertools/idempotencyimport { DynamoDBPersistenceLayer } from @awslambdapowertools/idempotency/dynamodbimport type { Context } from awslambdaimport type { Request Response SubscriptionResult } from /types
export const handler = makeIdempotent( async (event Request context Context) Promise =gt { try { const payment = // 创建支付
return { paymentId paymentid message success statusCode 200 }} catch (error) { throw new Error(创建支付时出错)}} { persistenceStore })
这个函数处理传入事件以创建 payment,并将 paymentId、message 和 status 返回给客户端。使 Lambda 函数处理程序幂等化确保支付只会被处理一次,尽管可能存在多次以相同事件负载的 Lambda 调用。您也可以将 makeIdempotent 函数包装器应用于处理程序之外的其他函数。
您可以通过添加一个 typests 文件来使用以下类型定义:
typescripttype Request = { user string productId string}
type Response = {
}
type SubscriptionResult = { id string productId string}
使用 middy 中介软体
如果您使用 middy 中介软体,Powertools 提供了 makeHandlerIdempotent 中介软体,以使 Lambda 函数处理程序幂等:
typescriptimport { makeHandlerIdempotent } from @awslambdapowertools/idempotency/middlewareimport { DynamoDBPersistenceLayer } from @awslambdapowertools/idempotency/dynamodbimport middy from @middy/coreimport type { Context } from awslambdaimport type { Request Response SubscriptionResult } from /types
const persistenceStore = new DynamoDBPersistenceLayer({ tableName IdempotencyTable})
export const handler = middy( async (event Request context Context) Promise =gt { try { const payment = // 创建支付对象
return { paymentId paymentid message success statusCode 200 }} catch (error) { throw new Error(创建支付时出错)}})use( makeHandlerIdempotent({ persistenceStore }))
配置选项
Powertools 幂等性工具提供了几个配置选项,以便根据使用案例场景来改变幂等性行为。本节将重点介绍最常用的配置选项。您可以在 AWS Powertools for Lambda (TypeScript) 文档 中找到所有可用的自定义选项。
持久性层选项
创建 DynamoDBPersistenceLayer 对象时,仅需要 tableName 属性。Powertools 将期望该表拥有一个 id 的分区键,并将生成其他属性以预设值提供。
如有需要,您可以通过传递选项参数来更改这些预设值:
typescriptimport { DynamoDBPersistenceLayer } from @awslambdapowertools/idempotency/dynamodb
const persistenceStore = new DynamoDBPersistenceLayer({ tableName idempotencyTableName keyAttr idempotencyKey // default id expiryAttr expiresAt // default expiration inProgressExpiryAttr inProgressExpiresAt // default inprogressexpiration statusAttr currentStatus // default status dataAttr resultData // default data validationKeyAttr validationKey // default validation})
使用事件负载的子集
在为 Lambda 函数处理程序配置幂等性时,Powertools 将使用整个事件负载进行幂等性处理,通过对对象进行哈希运算。
然而,来自 AWS 服务的事件,例如 Amazon API Gateway 或 Amazon Simple Queue Service (Amazon SQS),通常具有生成的字段,如 timestamp 或 requestId。这会导致 Powertools 将每个事件负载视为唯一。
为避免这种情况,请创建一个 IdempotencyConfig,并配置应哈希的事件负载部分。
创建 IdempotencyConfig 并设置 eventKeyJmespath 为事件负载中的一个键:
typescriptimport { IdempotencyConfig } from @awslambdapowertools/idempotency
// 从请求标头提取幂等性键const config = new IdempotencyConfig({ eventKeyJmesPath headersXIdempotencyKey})
使用 XIdempotencyKey 标头作为幂等性键。随后使用相同标头值的调用将会实现幂等。
然后,将此配置添加到先前示例中的 makeIdempotent 函数包装器中:
typescriptexport const handler = makeIdempotent( async (event Request context Context) Promise =gt { try { const payment = // 创建支付
return { paymentId paymentid message success statusCode 200 }} catch (error) { throw new Error(创建支付时出错)}} { persistenceStore config })

事件负载应包含 XIdempotencyKey 的标头,以便 Powertools 能够使用该字段进行幂等性处理:
json{ version 20 routeKey ANY /createpayment rawPath /createpayment rawQueryString headers { Header1 value1 XIdempotencyKey abcdefg } requestContext { accountId 123456789012 apiId apiid domainName idexecuteapiuseast1amazonawscom domainPrefix id http { method POST path /createpayment protocol HTTP/11 sourceIp ip userAgent agent } requestId id routeKey ANY /createpayment stage default time 10/Feb/2021134043 0000 timeEpoch 1612964443723 } body {userxyzproductId123456789} isBase64Encoded false}
您还可以应用其他配置选项,例如有效负载检查、到期持续时间、当地缓存等。请参见 Powertools for AWS Lambda (TypeScript) 文档 获取更多信息。
自定义 AWS SDK 配置
DynamoDBPersistenceLayer 是内建的,允许您存储所有请求的幂等性数据。在底层,Powertools 使用 AWS SDK for JavaScript v3。可以通过传递 clientConfig 对象来更改 SDK 配置。
以下示例将区域设置为 euwest1:
typescriptimport { DynamoDBPersistenceLayer } from @awslambdapowertools/idempotency/dynamodb
const persistenceStore = new DynamoDBPersistenceLayer({ tableName IdempotencyTable clientConfig { region euwest1 }})
如果您使用自己的客户端,可以将其传递给持久化层:
typescriptimport { DynamoDBPersistenceLayer } from @awslambdapowertools/idempotency/dynamodbimport { DynamoDBClient } from @awssdk/clientdynamodb
const ddbClient = new DynamoDBClient({ region euwest1 })
const dynamoDBPersistenceLayer = new DynamoDBPersistenceLayer({ tableName IdempotencyTable awsSdkV3Client ddbClient})
结论
使您的 Lambda 函数具备幂等性可能是一个挑战,如果处理不当,会导致重复数据、不一致情况以及不良的客户体验。本文展示了如何使用 Powertools for AWS Lambda (TypeScript) 来只对关键交易进行一次处理。
有关 Powertools 幂等性功能及其配置选项的更多详情,请参见完整 文档。
欲了解更多无伺服器学习资源,请访问 Serverless Land。
飞鱼加速器下载标签 贡献 无伺服器
发表评论