Redis:探索将 Redis 作为无服务器数据库来解决 API 中的幂等性
🛵 📬 Middy 的幂等中间件
Redis 意味着快速。 这一直是我的印象。但与此同时,对我来说,Redis“仅仅”(好像这还不够)是一个缓存存储。令我惊讶的是,Redis 的功能远不止于此。
这将是探索AWS 无服务器数据库现状 系列文章的第一篇 ,我选择了 Redis,只是想尝试一些不同于常见选择(DynamoDB)的东西(同时也想尝试一下第三方产品)。在接下来的几周里,我计划探索 AWS 上一些与无服务器架构配合使用的数据库产品,并探讨它们各自的优缺点。
无服务器 Redis
Redis 的定义 如下 :
Redis 是一个开源(BSD 许可)的内存数据结构存储,可用作数据库、缓存和消息代理。Redis 提供多种数据结构,例如字符串、哈希、列表、集合、支持范围查询的有序集合、位图、超日志、地理空间索引和流。
你可以 查阅几篇深入 描述每种数据结构的论文,以便做出更明智的决定。由于它是开源的,理论上我们可以在服务器中实现它,但如果继续走无服务器路线,那将是一个奇怪的,甚至是错误的选择。这里介绍的是 Uptash,一个基于 Redis 的无服务器数据库产品。在 “使用无服务器 Redis 进行微博” 中,你可以了解 CRUD 的工作原理;如果更注重框架,你可以观看 Lee Robinson 的 精彩视频,了解如何在 Next.js 中使用它:
VIDEO
foo
正如我在开篇文章中承诺的那样,我希望进行深入的探索,而不仅仅是我们那些 平庸的 bar
例子(顺便说一句,Upstash 的官方文档在这方面也存在问题)。对于 Redis,我几乎只停留在键/值存储类型,但这个解决方案可以解锁许多用例,因为它允许你访问 GraphQL API,并且你可以拥有一个文档齐全的资源管理器。
在开始之前:对我来说,设置 价格上限 和账单无意外始终是一个 优势 ,在这种情况下,您甚至无需提供信用卡信息即可开始使用。但是,当您超过免费套餐时,您需要输入信用卡信息,这与价格上限功能无关——这完全合理。我之前在 《致 AWS 的公开信:请给我们一个价格上限》 一文中讨论过这个问题,至少对我来说,作为一名居住在巴西的开发人员,AWS 极其忽视了这一点。
我的目标是尝试一些有价值的用例,同时深入探索数据处理,并希望解决一个非常重要的问题,至少对我来说是这样: 幂等性 。所以没有什么比 key
/ value
store更花哨的了,但我认为探索一个对于任何生产级函数来说都很重要的特性。另外,请注意,虽然这是Powertools库中的一个特性 NodeJS
,但我遵循的是官方Powertools库的步骤 Python
。我希望即使你不是这个 Node
生态系统的成员,也能从中获得一些价值。
幂等性:自己做
幂等运算可以重复任意次数,结果始终相同。在算术中,将零加到一个数字上就是幂等的。某些类型的 AWS Lambda 请求应该运行多次,当你的函数不是幂等的时,就会产生不一致的问题。AWS 有一个 页面介绍了如何实现这一急需的功能。是的,这很经典, 让你的 API 幂等 确实有一个非常重要的原因 。
为什么幂等性很重要:
你有一个 Lambda 函数,它通过 API 进行支付并保存到某个数据库中。如果其中一个成功,另一个失败,你的 Lambda 函数会 再次运行 ,最终你可能会遇到两个收银机,甚至更糟……你的客户会被重复收费。
您不想或不需要重新处理某些可能耗费大量时间或资源的请求。也许用户正在一次又一次地尝试执行某个操作。
让 API 具备幂等性 并不 像很多人想象的那么简单 。你可以先看看AWS 的 Lambda Powertools for Python ,它出色地解释了这个用例并给出了具体的实现方法。Malcolm Featonby 也写了一篇很棒的文章, 《 使用幂等 API 实现安全的重试》 ,它在 Amazon Builder's Library 中被列为 300 级架构 ,属于 高级 分类。
让我们从 Lambda Powertools 的 范围开始:
幂等性键 是整个事件或事件的特定配置子集的哈希表示,调用结果以 JSON 序列化形式存储在持久存储层中。
首先,我创建一个新的数据库。我使用的是 WordPress 著名的“5 分钟安装”功能。Upstash 声称只需 30 秒即可安装完成,这可不是开玩笑。
在这里您可以看到我选择启用 强一致性模式 。 强一致性 。强一致性提供最新数据,但代价是高延迟。最终一致性提供低延迟,但可能会使用过时的数据回复读取请求,因为数据库的所有节点可能都没有更新的数据。因为这是一个非常复杂的主题,而这个简短的解释只是非常简短地开始触及它的表面,我建议您阅读 Martin Kleppmann 的书 《设计数据密集型应用程序 : 可靠、可扩展和可维护系统背后的大创意》 。我通常大部分时间都 使用最终一致性 。我在这里选择强一致性,因为我想尽最大努力做到尽可能的幂等,否则我们可能会得到假阴性。
每个 Lambda 都会收到一个 event
作为其请求的一部分。
因此,我们在 lambda 函数中要做的第一件事就是创建整个对象的哈希表示 event
。我将使用 Node 原生 crypto
库。
const hash = crypto
. createHash ( ‘ sha256 ’ )
. update ( event )
. digest ( ‘ base64 ’ );
Enter fullscreen mode
Exit fullscreen mode
这将为给定事件生成一个唯一标识符,并将其用作 Redis 数据库中的唯一键。
我将利用 Middy ,这是 Node 中 Lambda 的轻量级中间件,它具有一个特别酷的功能(对于当前任务非常有用),即 类似洋葱的中间件模式实现,以及创建可以在处理程序 之后 和 之前 读取函数的中间件的能力 ,这对于幂等 API 至关重要。
在 之前 我们必须将其 event
变成哈希;
const createHash = ( event : any ): string => {
return crypto
. createHash ( “ sha256 ” )
. update ( JSON . stringify ( event ))
. digest ( “ base64 ” );
};
Enter fullscreen mode
Exit fullscreen mode
请注意,那里有一个 明确的 any — 神话中的、在生产中不存在的 any
。
然后,检查 Redis 数据库中该哈希值是否已存在于表中。我们将使用 lib 来完成此操作 ioredis
。我们将向正在创建的中间件传递一个选项。
//in the handler
import Redis from “ ioredis ” ;
// handler code
handler . use ( jsonBodyParser ()). use (
idempotent ({
client : new Redis ( process . env . UPSTASH_REDISS )
})
);
Enter fullscreen mode
Exit fullscreen mode
Redis 实例正在 rediss://
通过环境接收包含您的用户名和密码凭证的字符串。这 不是 最安全的方法,您可以将此 URL 存储在 AWS Systems Manager 中,然后以安全的方式导入,这样甚至可以根据需要轮换凭证。我在这里为了测试而使用了这种快捷方式,但我肯定您 永远 不会在生产环境中这样做,对吧?
无论如何,我们需要解析结果,因为我们将在下一个执行阶段将它们保存为字符串。
// in the middleware
const hash = createHash ( request . event );
const getByHash = await options . client . get ( hash );
if ( getByHash ) {
return JSON . parse ( getByHash );
}
Enter fullscreen mode
Exit fullscreen mode
如果发生错误,我们会收到一个 null
响应,这对于一个非常简单的检查来说非常棒。如果收到 null
,我们不需要做任何事情,函数将继续执行其他中间件、处理程序,然后执行 后续的 执行顺序。如果这个 get 不为 null ,我们必须返回 after 函数存储的响应 。
{
statusCode: 200 ,
body: ‘ { \n “data”: { \n “message”: “Hello from the other Side!”\n } \n } ’
}
Enter fullscreen mode
Exit fullscreen mode
然后我们将调用 return response
您的中间件。这将提前停止执行,并且不会传递给 lambda 的任何其他部分,因此该中间件需要是第一个(如果不是第一个可用的话)的中间件之一。
之后 我们必须保存该事件哈希的响应 。
const hash = createHash ( request . event );
const responseStr = JSON . stringify ( request . response );
await options . client . set ( hash , responseStr );
Enter fullscreen mode
Exit fullscreen mode
仅此而已。请注意,此阶段和执行发生在处理程序通过处理程序发送响应之后。
就是这样。请注意,此阶段和执行发生在处理程序通过处理程序发送响应之后。
是的, 它确实很快 。Upstash 自己的博客上发表了一篇名为《 无服务器数据库延迟比较》的 文章,之后 Hacker News 上也对此进行了讨论 。我没有做过基准测试,甚至没有计划这么做,但作为一名用户,我感觉它已经达到了极致的即时性。而这个功能无疑符合我的预期。
好了,各位,“就这样吧”。
开玩笑的,当然不是。但这只是一个开始。由于碰撞的可能性几乎为零(即两个哈希值相等),只有完全相同的请求才会得到相同的响应。但如果我们想检查某些键,例如 x-idempotence
请求头中的键,甚至是请求正文中的字段,我们也可以将其作为目标。
如果你想了解这个实现并寻求帮助,我把这段代码打包成 NodeJS Lambdas 代码,作为 Middy 的中间件提供给你使用。这个库接受一些选项,用于设置 headers、body 中的键和路径:
🛵 📬 适用于您的 AWS Lambdas 的 Idempotence Middy 中间件
🛵 📬 Middy 的幂等中间件
适用于 AWS Lambdas 的幂等 Middy 中间件
在🇧🇷 巴西开发
🛵 它做什么
Middy 是一个非常简单的中间件引擎,它允许您在使用 Node.js 时简化 AWS Lambda 代码。该中间件旨在简化幂等 API 的实现。
让 API 具备幂等性 并不像很多人想象的那么简单 ,你可以看看AWS 的 Lambda Powertools for Python ,它很好地解释了这个用例以及具体的实现方法。Malcolm Featonby 也写了一篇很棒的文章, 使用幂等 API 实现安全的重试 ,它在 Amazon Builder's Library 中被列为 300 级架构 ,属于 高级 分类。
🚀 安装
使用您最喜欢的包管理器:
yarn add middy-idempotent
Enter fullscreen mode
Exit fullscreen mode
npm install middy-idempotent -S
Enter fullscreen mode
Exit fullscreen mode
用法
此外 @middy/core
,您还必须使用 @middy/http-json-body-parser
这个中间件……
至于上面的库,您可以实现 SSM,这样它就不会将您的秘密字符串放置在您的基础设施环境中,但我计划至少在接下来的几天内添加另一个存储提供商 DynamoDB。
接下来,我将测试另一个无服务器数据库产品,虽然不是那种“Hello World”式的示例,但会用更实用、更有价值的用例——至少,这是我的希望!也请在评论区留下你的想法、见解和见解!
继续阅读:https://dev.to/aws-builders/redis-exploring-redis-as-serverless-database-to-solve-idempotence-in-apis-2gma