当前位置: 首页 > 知识库问答 >
问题:

在 AWS Lambda 实例之间协调单例令牌

赖浩荡
2023-03-14

考虑一个编写为AWS Lambda函数的聊天机器人。它通过来自第三方服务的HTTP请求被调用,第三方服务是它集成的聊天服务。现在,第三方API有点…古怪。它的主要问题是:它需要一个身份验证令牌才能与之交互,但任何时候只能存在一个令牌。它的工作方式是:

  • Bot创建一个带有一堆秘密信息的POST /auth请求,并返回一个令牌作为响应。
  • 然后,Bot向POST /messages发出请求,并带有授权:承载

关键是< code>POST /auth请求会重置任何以前的令牌,并使持有令牌的所有其他客户端无效。没有< code>GET /auth或类似的方法来获取现有令牌。

这现在变成了随机产生的机器人的并发实例之间的协调问题。它们都需要使用同一个令牌,但是会独立生成令牌。我不太热衷于引入一些任务只是协调令牌的单体服务,我希望保持独立伸缩的Lambda范式。我使用DynamoDB来存储令牌,试图在bot实例之间协调它们,但仍然存在边缘情况竞争条件,这可能需要最多三次重试才能解决令牌。

最坏的情况是存储在 DynamoDB 中的无效令牌和两个同时实例化的机器人:

  • 两者都将读取无效令牌
  • 两者都将尝试失败的请求
  • 两者都将尝试再次读取令牌,因为另一个机器人可能在此期间刷新了它
  • 两者都将丢弃已知是坏的令牌
  • 两者都会生成一个新的代币并存储,其中一个随机“获胜”
  • "失败者"将在做另一个糟糕的请求,重复前面的步骤

理想情况下,应该有一个可锁定的资源,一个机器人可以锁定并生成令牌,其他机器人可以等待。但是AFAIK DynamoDB没有那种功能。

有什么好的模式和/或AWS服务来协调独立的、并行的Lambda实例之间的单例令牌?

共有3个答案

晁聪
2023-03-14

虽然 DynamoDB 中没有实现本机锁定,但以下是 2017 年的 AWS 博客文章,其中展示了适用于 Java 开发工具包的 DynamoDB 锁定库。https://aws.amazon.com/blogs/database/building-distributed-locks-with-the-dynamodb-lock-client/

以下摘录对该机制有所启发:

DynamoDB锁客户端使用DynamoDB UpdateItem API来心跳和扩展每个主机拥有的锁。此外,锁客户端使用客户端TTL来过期锁。锁客户端使用最新版本的AWS SDKJava。锁定协议在锁的整个生命周期中广泛使用条件更新(创建、心跳更新和删除过期)。

有一些针对其他语言(如Python)的分支和改编。

尤钱明
2023-03-14

我想对“延斯”的回答提出一个稍微不同的看法:

>

  • 使用 DynamoDB 存储当前身份验证令牌:具有单个条目的 DDB 表可用作身份验证令牌的权威源。有关如何更新令牌的更多信息,请参阅下面的#3。

    auth令牌的本地缓存:虽然Lambda应该是无状态的,但它确实有能力进行一些本地缓存,所以每个Lambda实例都应该遵循如下逻辑:

    • 如果它在本地缓存中具有身份验证令牌的 NULL 值,请从 DDB 读取它并更新本地缓存
    • 如果它具有身份验证令牌的非 NULL 值,则尝试使用身份验证令牌发出请求
    • 如果请求因令牌无效而失败,请尝试执行令牌刷新(请参阅下面的 #3)

    利用条件写入来刷新令牌:为此,任何发现坏令牌的lambda实例都应该通过对DDB表中的令牌记录进行条件写入来尝试“指定自己”刷新令牌,尝试将自己声明为自己指定的更新者

      < li >如果条件写入成功,lambda继续发出< code>POST /auth请求,获取新令牌,随后更新DDB记录,并将其自身从锁定中移除; < li >如果条件写入失败,lambda应该后退一段时间(可能200毫秒左右),然后尝试重新读取记录,以查看在此期间另一个lambda是否成功刷新了令牌 < li >如果在读取记录时,lambda确定令牌已经被刷新(即令牌值不同于它所拥有的值),它可以继续使用新令牌,一切正常;如果发现记录仍被锁定,它可以再等待一段时间,然后重试 < li >您可能应该设定一个安全的回退时间段(比如1-2秒),在此之后,lambda应该“接管”更新令牌的责任,假设之前锁定它的另一个实例由于某种原因已经终止;这意味着您不会陷入所有实例都永远等待的死锁中

    这个解决方案的优点是减少了为每个请求从DDB读取数据的需要,并且不需要任何其他服务(除了Lambda和DDB)。

    执行条件写入时,lambda 可以为操作生成 UUID,并在项目上存储此 UUID 和时间戳,以指示项目被“锁定”的时间。后续实例将读取 UUID 和时间戳,并确定某个其他实例必须正在处理该项目。如果自项目锁定以来已经过了太多时间,则另一个实例可以“接管”。

  • 笪俊迈
    2023-03-14

    就个人而言,我会尝试将令牌的读写操作分离。应该只有一个“进程”写入令牌,而多个“进程”从您的存储中读取令牌。

    例如,一种解决方案是使用AWS EventBridge定期触发Lambda(比如每30分钟一次)。这个Lambda调用< code>/authendpoint,获取一个新的令牌,然后将其存储在DynamoDB中。

    然后,具有业务逻辑的Lambda应该使用一致/强读取来避免竞争情况(当另一个Lambda读取现在陈旧的令牌时,写入新的令牌)。

    您的消息 Lambda 中的业务逻辑现在只需从数据库中读取令牌,从而允许您根据需要“扩展”到任意数量的 Lambda,而无需它们相互干扰。

     类似资料:
    • 本文向大家介绍C#调用CMD命令实例,包括了C#调用CMD命令实例的使用技巧和注意事项,需要的朋友参考一下 有时候有一些DOS命令需要我们在执行程序的时候调用,这需要使用C#提供的相关接口。 代码如下,很简单,相信大家都能看懂,我就不赘述了。

    • browserfunctions.java login.java java

    • 本文向大家介绍Lua之协同程序coroutine代码实例,包括了Lua之协同程序coroutine代码实例的使用技巧和注意事项,需要的朋友参考一下 注: resume得到返回值, 如果有对应的yield在wait resume,那么yield的参数作为resum的返回值,第一个返回值表示coroutine没有错误,后面的返回值个数及其值视yeild参数而定。 如果没有yield在wait,那么返回

    • 本文向大家介绍php调用KyotoTycoon简单实例,包括了php调用KyotoTycoon简单实例的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了php调用KyotoTycoon的方法。分享给大家供大家参考。具体如下: Kyoto Tycoon(简称KT)是Tokyo Tyrant 的作者Mikio Hirabayashi 的系列作品之一,KT 是一个数据库网络层服务,它提供一个插件机

    • 本文向大家介绍JavaScript设计模式之单例模式实例,包括了JavaScript设计模式之单例模式实例的使用技巧和注意事项,需要的朋友参考一下 《Practical Common Lisp》的作者 Peter Seibel 曾说,如果你需要一种模式,那一定是哪里出了问题。他所说的问题是指因为语言的天生缺陷,不得不去寻求和总结一种通用的解决方案。 不管是弱类型或强类型,静态或动态语言,命令式或说

    • 我们一直在试图找出如何从groovy脚本调用,(0),(1)selenium网站登录,然后调用另一个测试用例中的导航(脚本)部分。两种方法都能100%独立运行。下面是我用来从(0)调用这两个脚本的代码,但一旦登录完成。运行,将使用选择打开一个新浏览器。运行,测试显然会失败,因为它没有登录。 因此,我去操作了登录、选择(通过删除webdriver代码),主脚本代码现在看起来是这样的(我已经在粘贴中排